react-native-fast-image 9.3.0 → 9.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -257,7 +257,7 @@ It can also be set in `style`; the prop wins. Either one overrides `resizeMode`.
257
257
 
258
258
  **Type:** `ResizeMode` · **Default:** `'cover'`
259
259
 
260
- **Deprecated:** Use `objectFit` instead, except for `'repeat'`, which has no `objectFit` value and isn't deprecated.
260
+ **Deprecated:** Use `objectFit` instead.
261
261
 
262
262
  How the image fills the view.
263
263
 
@@ -22,10 +22,13 @@ import java.util.ArrayList;
22
22
  import java.util.Arrays;
23
23
  import java.util.Comparator;
24
24
  import java.util.HashMap;
25
+ import java.util.HashSet;
26
+ import java.util.Iterator;
25
27
  import java.util.List;
26
28
  import java.util.Map;
27
- import java.util.PriorityQueue;
29
+ import java.util.Set;
28
30
  import java.util.TreeMap;
31
+ import java.util.TreeSet;
29
32
  import java.util.concurrent.ConcurrentHashMap;
30
33
  import java.util.concurrent.LinkedBlockingQueue;
31
34
  import java.util.concurrent.ThreadFactory;
@@ -64,13 +67,18 @@ import okio.BufferedSource;
64
67
  // once every request has read what it needed.
65
68
  //
66
69
  // At most MAX_RUNNING downloads run at once, as SDWebImage's downloader limits
67
- // them; the others wait to send their request, most urgent first (Glide's
68
- // priority: views before preloads). A running download's response is read as
69
- // soon as it arrives (not on OkHttp's dispatcher, which React Native's
70
- // networking shares): a response left unread on an HTTP/2 connection would
71
- // hold back the others on it. A download that stalls fails like any other,
72
- // with the timeouts of the clients (FastImageOkHttpProgressGlideModule), which
73
- // only count once it has started.
70
+ // them, and at most as many wait for their response from a host as the
71
+ // client's dispatcher sends to a host at once (5 by default): OkHttp would
72
+ // hold the others in the order they came, where a more urgent request can't
73
+ // move them up. The others wait to send their request, most urgent first
74
+ // (Glide's priority: views before preloads), and the most urgent one waiting
75
+ // for a host's room keeps a running slot, so less urgent downloads to other
76
+ // hosts can't take them all. A running download's response is read as soon
77
+ // as it arrives, on its own thread (OkHttp counts a request to its host until
78
+ // its callback returns): a response left unread on an HTTP/2 connection
79
+ // would hold back the others on it. A download that stalls fails like any
80
+ // other, with the timeouts of the clients (FastImageOkHttpProgressGlideModule),
81
+ // which only count once it has started.
74
82
  final class FastImageSharedDownloads {
75
83
  // The longest response kept in memory, and the most bytes held in memory
76
84
  // for downloads at once: encoded images (not decoded bitmaps, which
@@ -98,9 +106,9 @@ final class FastImageSharedDownloads {
98
106
  // lock).
99
107
  private static final Map<String, Download> downloads = new HashMap<>();
100
108
  // Guarded by downloads: downloads waiting to start, most urgent first, then
101
- // in the order they were asked for; how many are running; and the bytes
102
- // held in memory.
103
- private static final PriorityQueue<Download> queue = new PriorityQueue<>(11, new Comparator<Download>() {
109
+ // in the order they were asked for; how many are running, and how many
110
+ // wait for their response from each host; and the bytes held in memory.
111
+ private static final TreeSet<Download> queue = new TreeSet<>(new Comparator<Download>() {
104
112
  @Override
105
113
  public int compare(Download a, Download b) {
106
114
  int byPriority = a.priority.compareTo(b.priority);
@@ -109,6 +117,7 @@ final class FastImageSharedDownloads {
109
117
  });
110
118
  private static long orders = 0;
111
119
  private static int running = 0;
120
+ private static final Map<String, Integer> sentTo = new HashMap<>();
112
121
  private static long held = 0;
113
122
  // Stores not yet released (see busy()).
114
123
  private static final AtomicInteger open = new AtomicInteger();
@@ -165,9 +174,10 @@ final class FastImageSharedDownloads {
165
174
  static String busy() {
166
175
  synchronized (downloads) {
167
176
  int stores = open.get();
168
- if (downloads.isEmpty() && queue.isEmpty() && running == 0 && held == 0 && stores == 0) return null;
177
+ if (downloads.isEmpty() && queue.isEmpty() && running == 0 && sentTo.isEmpty() && held == 0
178
+ && stores == 0) return null;
169
179
  return downloads.size() + " downloads, " + queue.size() + " waiting, " + running + " running, "
170
- + held + " bytes in memory, " + stores + " stores open";
180
+ + sentTo + " waiting for a response, " + held + " bytes in memory, " + stores + " stores open";
171
181
  }
172
182
  }
173
183
 
@@ -253,13 +263,25 @@ final class FastImageSharedDownloads {
253
263
  }
254
264
  }
255
265
 
256
- // Starts the downloads waiting that can run now.
266
+ // Starts the downloads waiting that can run now, most urgent first: those
267
+ // whose host has room, leaving a slot for each host without (see above).
257
268
  private static void startQueued() {
258
269
  List<Download> starting = new ArrayList<>();
259
270
  List<Request> requests = new ArrayList<>();
260
271
  synchronized (downloads) {
261
- while (running < MAX_RUNNING && !queue.isEmpty()) {
262
- Download download = queue.poll();
272
+ Set<String> full = new HashSet<>();
273
+ Iterator<Download> waiting = queue.iterator();
274
+ while (running + full.size() < MAX_RUNNING && waiting.hasNext()) {
275
+ Download download = waiting.next();
276
+ String host = download.request.url().host();
277
+ Integer sent = sentTo.get(host);
278
+ if (sent != null && sent >= download.client.dispatcher().getMaxRequestsPerHost()) {
279
+ full.add(host);
280
+ continue;
281
+ }
282
+ waiting.remove();
283
+ sentTo.put(host, sent == null ? 1 : sent + 1);
284
+ download.host = host;
263
285
  download.queued = false;
264
286
  download.running = true;
265
287
  running++;
@@ -539,6 +561,10 @@ final class FastImageSharedDownloads {
539
561
  private final long order;
540
562
  private boolean queued = false;
541
563
  private boolean running = false;
564
+ // Guarded by downloads. The host its request was sent to, until its
565
+ // response (or failure) arrives.
566
+ @Nullable
567
+ private String host;
542
568
  // Guarded by this. The call, once started, and whether the download
543
569
  // was cancelled (before it started, it doesn't).
544
570
  @Nullable
@@ -585,7 +611,9 @@ final class FastImageSharedDownloads {
585
611
  return;
586
612
  }
587
613
  Download download;
588
- boolean queuedOne = false;
614
+ // Queued one, or moved a waiting one to another url (whose host
615
+ // can have room).
616
+ boolean queueChanged = false;
589
617
  InputStream ready = null;
590
618
  synchronized (downloads) {
591
619
  // Cancelled before it got here.
@@ -601,7 +629,7 @@ final class FastImageSharedDownloads {
601
629
  downloads.put(fetcher.key, download);
602
630
  download.queued = true;
603
631
  queue.add(download);
604
- queuedOne = true;
632
+ queueChanged = true;
605
633
  } else if (download.queued) {
606
634
  // A more urgent request moves it up (until it leaves).
607
635
  if (fetcher.priority.compareTo(download.priority) < 0) download.reprioritize(fetcher.priority);
@@ -612,6 +640,7 @@ final class FastImageSharedDownloads {
612
640
  download.url = fetcher.url;
613
641
  download.client = fetcher.client;
614
642
  download.request = request;
643
+ queueChanged = true;
615
644
  }
616
645
  }
617
646
  fetcher.download = download;
@@ -627,7 +656,7 @@ final class FastImageSharedDownloads {
627
656
  }
628
657
  }
629
658
  if (ready != null) fetcher.deliver(ready, callback);
630
- if (queuedOne) startQueued();
659
+ if (queueChanged) startQueued();
631
660
  }
632
661
 
633
662
  // Sends the request (off the main thread, and outside the locks:
@@ -656,10 +685,24 @@ final class FastImageSharedDownloads {
656
685
  if (started != null) started.cancel();
657
686
  }
658
687
 
688
+ // With downloads locked: its request has its response, failed, or
689
+ // wasn't sent, so another can be sent to its host.
690
+ private void responded() {
691
+ if (host == null) return;
692
+ int sent = sentTo.get(host) - 1;
693
+ if (sent == 0) {
694
+ sentTo.remove(host);
695
+ } else {
696
+ sentTo.put(host, sent);
697
+ }
698
+ host = null;
699
+ }
700
+
659
701
  // It no longer runs: the next download waiting can start.
660
702
  private void stopped() {
661
703
  synchronized (downloads) {
662
704
  if (!running) return;
705
+ responded();
663
706
  running = false;
664
707
  FastImageSharedDownloads.running--;
665
708
  }
@@ -853,6 +896,7 @@ final class FastImageSharedDownloads {
853
896
  fromHttpCache = response.networkResponse() == null && response.cacheResponse() != null;
854
897
  final long length = body.contentLength();
855
898
  synchronized (downloads) {
899
+ responded();
856
900
  // No progress without a length.
857
901
  if (length <= 0) silent = true;
858
902
  }
@@ -862,6 +906,7 @@ final class FastImageSharedDownloads {
862
906
  readInto(body, length);
863
907
  }
864
908
  });
909
+ startQueued();
865
910
  }
866
911
 
867
912
  // Sends a progress step, unless the download has stopped: checked and
@@ -832,6 +832,9 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
832
832
  private Object mModel;
833
833
  // Whether the loading image has loaded or failed.
834
834
  private boolean mLoadEnded = false;
835
+ // Whether the loading request is the image that's showing loaded again
836
+ // (reloadForSize's or reblur's), which sends no events.
837
+ private boolean mLoadingQuietly = false;
835
838
  // Set by reblur: the next load is the same one again, so it doesn't send
836
839
  // onLoadStart (it sends its other events, as the load it replaces would
837
840
  // have).
@@ -942,13 +945,16 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
942
945
  .error(current.clone())
943
946
  .listener(new FastImageRequestListener(null, true, false));
944
947
  if (meanwhile == null) builder = builder.thumbnail(current);
945
- into(shown, builder, meanwhile);
948
+ into(shown, builder, meanwhile, true);
946
949
  }
947
950
 
948
951
  // blurRadius changed, and nothing else that needs a reload: the image
949
952
  // that's showing, blurred again, with no events, as reloadForSize does: it's
950
953
  // decoded again from the cache and blurred on Glide's threads, and the view
951
- // keeps the current image until then (or if that fails). A load that
954
+ // keeps the current image until then. If that fails, it shows the image
955
+ // from Glide's caches; cache: 'web' skips them, so the view shows
956
+ // defaultSource (or nothing) then. Also while it's loading again that way
957
+ // (e.g. the blur changes every frame): that load is replaced. A load that
952
958
  // hasn't finished starts again with the new blur, without a second
953
959
  // onLoadStart. Nothing loaded (defaultSource, which isn't blurred, or
954
960
  // nothing): the next load has the new blur.
@@ -956,7 +962,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
956
962
  private void reblur() {
957
963
  if (requestManager == null || mBaseRequest == null) return;
958
964
  RequestBuilder<Drawable> shown = mShownRequest;
959
- if (shown != null && shown == mLoadingRequest) {
965
+ if (shown != null && (shown == mLoadingRequest || mLoadingQuietly)) {
960
966
  RequestBuilder<Drawable> request = mBaseRequest.clone().apply(renderingOptions(mModel));
961
967
  RequestBuilder<Drawable> current = fromCache(shown);
962
968
  Drawable meanwhile = shownCopy(shown);
@@ -966,7 +972,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
966
972
  .error(current.clone())
967
973
  .listener(new FastImageRequestListener(null, true, false));
968
974
  if (meanwhile == null) builder = builder.thumbnail(current);
969
- into(request, builder, meanwhile);
975
+ into(request, builder, meanwhile, true);
970
976
  } else if (mLoadingRequest != null && !mLoadEnded) {
971
977
  mRestarting = true;
972
978
  mNeedsReload = true;
@@ -1007,10 +1013,13 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
1007
1013
 
1008
1014
  // Starts loading the request (built by builder), showing meanwhile (if
1009
1015
  // not null) until it loads, and recording the size Glide loads it at. The size callback is added first, so it has the size by
1010
- // the time the image loads, even from the memory cache.
1011
- private void into(RequestBuilder<Drawable> request, RequestBuilder<Drawable> builder, @Nullable Drawable meanwhile) {
1016
+ // the time the image loads, even from the memory cache. Quiet: the image
1017
+ // that's showing, loaded again (no events).
1018
+ private void into(RequestBuilder<Drawable> request, RequestBuilder<Drawable> builder, @Nullable Drawable meanwhile,
1019
+ boolean quiet) {
1012
1020
  final int load = mLoadCount;
1013
1021
  mLoadingRequest = request;
1022
+ mLoadingQuietly = quiet;
1014
1023
  mLoadEnded = false;
1015
1024
  mLoadingWidth = 0;
1016
1025
  mLoadingHeight = 0;
@@ -1218,7 +1227,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
1218
1227
  if (imageSource != null)
1219
1228
  builder.listener(new FastImageRequestListener(imageSource, thumbnail, true));
1220
1229
 
1221
- into(request, builder, meanwhile);
1230
+ into(request, builder, meanwhile, false);
1222
1231
  }
1223
1232
  }
1224
1233
 
@@ -125,7 +125,7 @@ export type FastImageProps = $ReadOnly<{|
125
125
 
126
126
  tintColor?: number | string,
127
127
  objectFit?: ?ObjectFit,
128
- // Deprecated: objectFit, except for 'repeat'.
128
+ // Deprecated: use objectFit.
129
129
  resizeMode?: ?ResizeModes,
130
130
  fallback?: ?boolean,
131
131
  recyclingKey?: ?string,
@@ -125,7 +125,7 @@ export type FastImageProps = $ReadOnly<{|
125
125
 
126
126
  tintColor?: number | string,
127
127
  objectFit?: ?ObjectFit,
128
- // Deprecated: objectFit, except for 'repeat'.
128
+ // Deprecated: use objectFit.
129
129
  resizeMode?: ?ResizeModes,
130
130
  fallback?: ?boolean,
131
131
  recyclingKey?: ?string,
package/dist/index.d.ts CHANGED
@@ -329,8 +329,7 @@ export interface FastImageProps extends AccessibilityProps, ViewProps {
329
329
  * image repeats its first frame, and `defaultSource` repeats too.
330
330
  *
331
331
  * @default 'cover'
332
- * @deprecated Use `objectFit` instead, except for `'repeat'`, which has no
333
- * `objectFit` value and isn't deprecated.
332
+ * @deprecated Use `objectFit` instead.
334
333
  */
335
334
  resizeMode?: ResizeMode;
336
335
  /**
@@ -568,8 +567,7 @@ export interface PreloadFailure {
568
567
  export type PreloadResult = PreloadSuccess | PreloadFailure;
569
568
  export interface FastImageStaticProperties {
570
569
  /**
571
- * @deprecated Use the `objectFit` prop instead of `resizeMode`, except for
572
- * `'repeat'`.
570
+ * @deprecated Use the `objectFit` prop instead of `resizeMode`.
573
571
  */
574
572
  resizeMode: typeof resizeMode;
575
573
  priority: typeof priority;
@@ -125,7 +125,7 @@ export type FastImageProps = $ReadOnly<{|
125
125
 
126
126
  tintColor?: number | string,
127
127
  objectFit?: ?ObjectFit,
128
- // Deprecated: objectFit, except for 'repeat'.
128
+ // Deprecated: use objectFit.
129
129
  resizeMode?: ?ResizeModes,
130
130
  fallback?: ?boolean,
131
131
  recyclingKey?: ?string,
@@ -125,7 +125,7 @@ export type FastImageProps = $ReadOnly<{|
125
125
 
126
126
  tintColor?: number | string,
127
127
  objectFit?: ?ObjectFit,
128
- // Deprecated: objectFit, except for 'repeat'.
128
+ // Deprecated: use objectFit.
129
129
  resizeMode?: ?ResizeModes,
130
130
  fallback?: ?boolean,
131
131
  recyclingKey?: ?string,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-fast-image",
3
- "version": "9.3.0",
3
+ "version": "9.3.2",
4
4
  "description": "FastImage, performant React Native image component.",
5
5
  "keywords": [
6
6
  "cache",