|
@@ -1,12 +1,12 @@
|
|
-/*
|
|
|
|
|
|
+/**
|
|
* Copyright (C) 2013 The Android Open Source Project
|
|
* Copyright (C) 2013 The Android Open Source Project
|
|
*
|
|
*
|
|
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
|
|
|
|
|
|
+ * <p>Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of the License at
|
|
* except in compliance with the License. You may obtain a copy of the License at
|
|
*
|
|
*
|
|
- * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
+ * <p>http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
*
|
|
- * Unless required by applicable law or agreed to in writing, software distributed under the
|
|
|
|
|
|
+ * <p>Unless required by applicable law or agreed to in writing, software distributed under the
|
|
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
|
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
|
|
* express or implied. See the License for the specific language governing permissions and
|
|
* express or implied. See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
* limitations under the License.
|
|
@@ -17,77 +17,59 @@ import android.graphics.Bitmap;
|
|
import android.graphics.Bitmap.Config;
|
|
import android.graphics.Bitmap.Config;
|
|
import android.os.Handler;
|
|
import android.os.Handler;
|
|
import android.os.Looper;
|
|
import android.os.Looper;
|
|
-import android.support.annotation.MainThread;
|
|
|
|
import android.widget.ImageView;
|
|
import android.widget.ImageView;
|
|
import android.widget.ImageView.ScaleType;
|
|
import android.widget.ImageView.ScaleType;
|
|
|
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
+import java.util.HashMap;
|
|
|
|
+import java.util.List;
|
|
|
|
+
|
|
import cn.yyxx.support.volley.source.Request;
|
|
import cn.yyxx.support.volley.source.Request;
|
|
import cn.yyxx.support.volley.source.RequestQueue;
|
|
import cn.yyxx.support.volley.source.RequestQueue;
|
|
import cn.yyxx.support.volley.source.Response.ErrorListener;
|
|
import cn.yyxx.support.volley.source.Response.ErrorListener;
|
|
import cn.yyxx.support.volley.source.Response.Listener;
|
|
import cn.yyxx.support.volley.source.Response.Listener;
|
|
-import cn.yyxx.support.volley.source.ResponseDelivery;
|
|
|
|
import cn.yyxx.support.volley.source.VolleyError;
|
|
import cn.yyxx.support.volley.source.VolleyError;
|
|
|
|
|
|
-import java.util.ArrayList;
|
|
|
|
-import java.util.HashMap;
|
|
|
|
-import java.util.List;
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* Helper that handles loading and caching images from remote URLs.
|
|
* Helper that handles loading and caching images from remote URLs.
|
|
*
|
|
*
|
|
* <p>The simple way to use this class is to call {@link ImageLoader#get(String, ImageListener)} and
|
|
* <p>The simple way to use this class is to call {@link ImageLoader#get(String, ImageListener)} and
|
|
* to pass in the default image listener provided by {@link ImageLoader#getImageListener(ImageView,
|
|
* to pass in the default image listener provided by {@link ImageLoader#getImageListener(ImageView,
|
|
- * int, int)}. Note that all function calls to this class must be made from the main thread, and all
|
|
|
|
- * responses will be delivered to the main thread as well. Custom {@link ResponseDelivery}s which
|
|
|
|
- * don't use the main thread are not supported.
|
|
|
|
|
|
+ * int, int)}. Note that all function calls to this class must be made from the main thead, and all
|
|
|
|
+ * responses will be delivered to the main thread as well.
|
|
*/
|
|
*/
|
|
public class ImageLoader {
|
|
public class ImageLoader {
|
|
/**
|
|
/**
|
|
* RequestQueue for dispatching ImageRequests onto.
|
|
* RequestQueue for dispatching ImageRequests onto.
|
|
*/
|
|
*/
|
|
private final RequestQueue mRequestQueue;
|
|
private final RequestQueue mRequestQueue;
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Amount of time to wait after first response arrives before delivering all responses.
|
|
|
|
- */
|
|
|
|
- private int mBatchResponseDelayMs = 100;
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* The cache implementation to be used as an L1 cache before calling into volley.
|
|
* The cache implementation to be used as an L1 cache before calling into volley.
|
|
*/
|
|
*/
|
|
private final ImageCache mCache;
|
|
private final ImageCache mCache;
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* HashMap of Cache keys -> BatchedImageRequest used to track in-flight requests so that we can
|
|
* HashMap of Cache keys -> BatchedImageRequest used to track in-flight requests so that we can
|
|
* coalesce multiple requests to the same URL into a single network request.
|
|
* coalesce multiple requests to the same URL into a single network request.
|
|
*/
|
|
*/
|
|
- private final HashMap<String, BatchedImageRequest> mInFlightRequests = new HashMap<>();
|
|
|
|
-
|
|
|
|
|
|
+ private final HashMap<String, BatchedImageRequest> mInFlightRequests =
|
|
|
|
+ new HashMap<String, BatchedImageRequest>();
|
|
/**
|
|
/**
|
|
* HashMap of the currently pending responses (waiting to be delivered).
|
|
* HashMap of the currently pending responses (waiting to be delivered).
|
|
*/
|
|
*/
|
|
- private final HashMap<String, BatchedImageRequest> mBatchedResponses = new HashMap<>();
|
|
|
|
-
|
|
|
|
|
|
+ private final HashMap<String, BatchedImageRequest> mBatchedResponses =
|
|
|
|
+ new HashMap<String, BatchedImageRequest>();
|
|
/**
|
|
/**
|
|
* Handler to the main thread.
|
|
* Handler to the main thread.
|
|
*/
|
|
*/
|
|
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
|
private final Handler mHandler = new Handler(Looper.getMainLooper());
|
|
-
|
|
|
|
/**
|
|
/**
|
|
- * Runnable for in-flight response delivery.
|
|
|
|
|
|
+ * Amount of time to wait after first response arrives before delivering all responses.
|
|
*/
|
|
*/
|
|
- private Runnable mRunnable;
|
|
|
|
-
|
|
|
|
|
|
+ private int mBatchResponseDelayMs = 100;
|
|
/**
|
|
/**
|
|
- * Simple cache adapter interface. If provided to the ImageLoader, it will be used as an L1
|
|
|
|
- * cache before dispatch to Volley. Implementations must not block. Implementation with an
|
|
|
|
- * LruCache is recommended.
|
|
|
|
|
|
+ * Runnable for in-flight response delivery.
|
|
*/
|
|
*/
|
|
- public interface ImageCache {
|
|
|
|
- Bitmap getBitmap(String url);
|
|
|
|
-
|
|
|
|
- void putBitmap(String url, Bitmap bitmap);
|
|
|
|
- }
|
|
|
|
|
|
+ private Runnable mRunnable;
|
|
|
|
|
|
/**
|
|
/**
|
|
* Constructs a new ImageLoader.
|
|
* Constructs a new ImageLoader.
|
|
@@ -131,27 +113,24 @@ public class ImageLoader {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Interface for the response handlers on image requests.
|
|
|
|
- *
|
|
|
|
- * <p>The call flow is this: 1. Upon being attached to a request, onResponse(response, true)
|
|
|
|
- * will be invoked to reflect any cached data that was already available. If the data was
|
|
|
|
- * available, response.getBitmap() will be non-null.
|
|
|
|
|
|
+ * Creates a cache key for use with the L1 cache.
|
|
*
|
|
*
|
|
- * <p>2. After a network response returns, only one of the following cases will happen: -
|
|
|
|
- * onResponse(response, false) will be called if the image was loaded. or - onErrorResponse will
|
|
|
|
- * be called if there was an error loading the image.
|
|
|
|
|
|
+ * @param url The URL of the request.
|
|
|
|
+ * @param maxWidth The max-width of the output.
|
|
|
|
+ * @param maxHeight The max-height of the output.
|
|
|
|
+ * @param scaleType The scaleType of the imageView.
|
|
*/
|
|
*/
|
|
- public interface ImageListener extends ErrorListener {
|
|
|
|
- /**
|
|
|
|
- * Listens for non-error changes to the loading of the image request.
|
|
|
|
- *
|
|
|
|
- * @param response Holds all information pertaining to the request, as well as the bitmap
|
|
|
|
- * (if it is loaded).
|
|
|
|
- * @param isImmediate True if this was called during ImageLoader.get() variants. This can be
|
|
|
|
- * used to differentiate between a cached image loading and a network image loading in
|
|
|
|
- * order to, for example, run an animation to fade in network loaded images.
|
|
|
|
- */
|
|
|
|
- void onResponse(ImageContainer response, boolean isImmediate);
|
|
|
|
|
|
+ private static String getCacheKey(
|
|
|
|
+ String url, int maxWidth, int maxHeight, ScaleType scaleType) {
|
|
|
|
+ return new StringBuilder(url.length() + 12)
|
|
|
|
+ .append("#W")
|
|
|
|
+ .append(maxWidth)
|
|
|
|
+ .append("#H")
|
|
|
|
+ .append(maxHeight)
|
|
|
|
+ .append("#S")
|
|
|
|
+ .append(scaleType.ordinal())
|
|
|
|
+ .append(url)
|
|
|
|
+ .toString();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -169,17 +148,14 @@ public class ImageLoader {
|
|
/**
|
|
/**
|
|
* Checks if the item is available in the cache.
|
|
* Checks if the item is available in the cache.
|
|
*
|
|
*
|
|
- * <p>Must be called from the main thread.
|
|
|
|
- *
|
|
|
|
* @param requestUrl The url of the remote image
|
|
* @param requestUrl The url of the remote image
|
|
* @param maxWidth The maximum width of the returned image.
|
|
* @param maxWidth The maximum width of the returned image.
|
|
* @param maxHeight The maximum height of the returned image.
|
|
* @param maxHeight The maximum height of the returned image.
|
|
* @param scaleType The scaleType of the imageView.
|
|
* @param scaleType The scaleType of the imageView.
|
|
* @return True if the item exists in cache, false otherwise.
|
|
* @return True if the item exists in cache, false otherwise.
|
|
*/
|
|
*/
|
|
- @MainThread
|
|
|
|
public boolean isCached(String requestUrl, int maxWidth, int maxHeight, ScaleType scaleType) {
|
|
public boolean isCached(String requestUrl, int maxWidth, int maxHeight, ScaleType scaleType) {
|
|
- Threads.throwIfNotOnMainThread();
|
|
|
|
|
|
+ throwIfNotOnMainThread();
|
|
|
|
|
|
String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);
|
|
String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);
|
|
return mCache.getBitmap(cacheKey) != null;
|
|
return mCache.getBitmap(cacheKey) != null;
|
|
@@ -195,7 +171,7 @@ public class ImageLoader {
|
|
* @param requestUrl The URL of the image to be loaded.
|
|
* @param requestUrl The URL of the image to be loaded.
|
|
*/
|
|
*/
|
|
public ImageContainer get(String requestUrl, final ImageListener listener) {
|
|
public ImageContainer get(String requestUrl, final ImageListener listener) {
|
|
- return get(requestUrl, listener, /* maxWidth= */ 0, /* maxHeight= */ 0);
|
|
|
|
|
|
+ return get(requestUrl, listener, 0, 0);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -212,8 +188,6 @@ public class ImageLoader {
|
|
* returns a bitmap container that contains all of the data relating to the request (as well as
|
|
* returns a bitmap container that contains all of the data relating to the request (as well as
|
|
* the default image if the requested image is not available).
|
|
* the default image if the requested image is not available).
|
|
*
|
|
*
|
|
- * <p>Must be called from the main thread.
|
|
|
|
- *
|
|
|
|
* @param requestUrl The url of the remote image
|
|
* @param requestUrl The url of the remote image
|
|
* @param imageListener The listener to call when the remote image is loaded
|
|
* @param imageListener The listener to call when the remote image is loaded
|
|
* @param maxWidth The maximum width of the returned image.
|
|
* @param maxWidth The maximum width of the returned image.
|
|
@@ -222,7 +196,6 @@ public class ImageLoader {
|
|
* @return A container object that contains all of the properties of the request, as well as the
|
|
* @return A container object that contains all of the properties of the request, as well as the
|
|
* currently available image (default if remote is not loaded).
|
|
* currently available image (default if remote is not loaded).
|
|
*/
|
|
*/
|
|
- @MainThread
|
|
|
|
public ImageContainer get(
|
|
public ImageContainer get(
|
|
String requestUrl,
|
|
String requestUrl,
|
|
ImageListener imageListener,
|
|
ImageListener imageListener,
|
|
@@ -231,7 +204,7 @@ public class ImageLoader {
|
|
ScaleType scaleType) {
|
|
ScaleType scaleType) {
|
|
|
|
|
|
// only fulfill requests that were initiated from the main thread.
|
|
// only fulfill requests that were initiated from the main thread.
|
|
- Threads.throwIfNotOnMainThread();
|
|
|
|
|
|
+ throwIfNotOnMainThread();
|
|
|
|
|
|
final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);
|
|
final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);
|
|
|
|
|
|
@@ -239,9 +212,7 @@ public class ImageLoader {
|
|
Bitmap cachedBitmap = mCache.getBitmap(cacheKey);
|
|
Bitmap cachedBitmap = mCache.getBitmap(cacheKey);
|
|
if (cachedBitmap != null) {
|
|
if (cachedBitmap != null) {
|
|
// Return the cached bitmap.
|
|
// Return the cached bitmap.
|
|
- ImageContainer container =
|
|
|
|
- new ImageContainer(
|
|
|
|
- cachedBitmap, requestUrl, /* cacheKey= */ null, /* listener= */ null);
|
|
|
|
|
|
+ ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null);
|
|
imageListener.onResponse(container, true);
|
|
imageListener.onResponse(container, true);
|
|
return container;
|
|
return container;
|
|
}
|
|
}
|
|
@@ -253,11 +224,8 @@ public class ImageLoader {
|
|
// Update the caller to let them know that they should use the default bitmap.
|
|
// Update the caller to let them know that they should use the default bitmap.
|
|
imageListener.onResponse(imageContainer, true);
|
|
imageListener.onResponse(imageContainer, true);
|
|
|
|
|
|
- // Check to see if a request is already in-flight or completed but pending batch delivery.
|
|
|
|
|
|
+ // Check to see if a request is already in-flight.
|
|
BatchedImageRequest request = mInFlightRequests.get(cacheKey);
|
|
BatchedImageRequest request = mInFlightRequests.get(cacheKey);
|
|
- if (request == null) {
|
|
|
|
- request = mBatchedResponses.get(cacheKey);
|
|
|
|
- }
|
|
|
|
if (request != null) {
|
|
if (request != null) {
|
|
// If it is, add this request to the list of listeners.
|
|
// If it is, add this request to the list of listeners.
|
|
request.addContainer(imageContainer);
|
|
request.addContainer(imageContainer);
|
|
@@ -352,86 +320,85 @@ public class ImageLoader {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Container object for all of the data surrounding an image request.
|
|
|
|
|
|
+ * Starts the runnable for batched delivery of responses if it is not already started.
|
|
|
|
+ *
|
|
|
|
+ * @param cacheKey The cacheKey of the response being delivered.
|
|
|
|
+ * @param request The BatchedImageRequest to be delivered.
|
|
*/
|
|
*/
|
|
- public class ImageContainer {
|
|
|
|
- /**
|
|
|
|
- * The most relevant bitmap for the container. If the image was in cache, the Holder to use
|
|
|
|
- * for the final bitmap (the one that pairs to the requested URL).
|
|
|
|
- */
|
|
|
|
- private Bitmap mBitmap;
|
|
|
|
-
|
|
|
|
- private final ImageListener mListener;
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * The cache key that was associated with the request
|
|
|
|
- */
|
|
|
|
- private final String mCacheKey;
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * The request URL that was specified
|
|
|
|
- */
|
|
|
|
- private final String mRequestUrl;
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Constructs a BitmapContainer object.
|
|
|
|
- *
|
|
|
|
- * @param bitmap The final bitmap (if it exists).
|
|
|
|
- * @param requestUrl The requested URL for this container.
|
|
|
|
- * @param cacheKey The cache key that identifies the requested URL for this container.
|
|
|
|
- */
|
|
|
|
- public ImageContainer(
|
|
|
|
- Bitmap bitmap, String requestUrl, String cacheKey, ImageListener listener) {
|
|
|
|
- mBitmap = bitmap;
|
|
|
|
- mRequestUrl = requestUrl;
|
|
|
|
- mCacheKey = cacheKey;
|
|
|
|
- mListener = listener;
|
|
|
|
|
|
+ private void batchResponse(String cacheKey, BatchedImageRequest request) {
|
|
|
|
+ mBatchedResponses.put(cacheKey, request);
|
|
|
|
+ // If we don't already have a batch delivery runnable in flight, make a new one.
|
|
|
|
+ // Note that this will be used to deliver responses to all callers in mBatchedResponses.
|
|
|
|
+ if (mRunnable == null) {
|
|
|
|
+ mRunnable =
|
|
|
|
+ new Runnable() {
|
|
|
|
+ @Override
|
|
|
|
+ public void run() {
|
|
|
|
+ for (BatchedImageRequest bir : mBatchedResponses.values()) {
|
|
|
|
+ for (ImageContainer container : bir.mContainers) {
|
|
|
|
+ // If one of the callers in the batched request canceled the
|
|
|
|
+ // request
|
|
|
|
+ // after the response was received but before it was delivered,
|
|
|
|
+ // skip them.
|
|
|
|
+ if (container.mListener == null) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ if (bir.getError() == null) {
|
|
|
|
+ container.mBitmap = bir.mResponseBitmap;
|
|
|
|
+ container.mListener.onResponse(container, false);
|
|
|
|
+ } else {
|
|
|
|
+ container.mListener.onErrorResponse(bir.getError());
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ mBatchedResponses.clear();
|
|
|
|
+ mRunnable = null;
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+ // Post the runnable.
|
|
|
|
+ mHandler.postDelayed(mRunnable, mBatchResponseDelayMs);
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
|
- * Releases interest in the in-flight request (and cancels it if no one else is listening).
|
|
|
|
- *
|
|
|
|
- * <p>Must be called from the main thread.
|
|
|
|
- */
|
|
|
|
- @MainThread
|
|
|
|
- public void cancelRequest() {
|
|
|
|
- Threads.throwIfNotOnMainThread();
|
|
|
|
-
|
|
|
|
- if (mListener == null) {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- BatchedImageRequest request = mInFlightRequests.get(mCacheKey);
|
|
|
|
- if (request != null) {
|
|
|
|
- boolean canceled = request.removeContainerAndCancelIfNecessary(this);
|
|
|
|
- if (canceled) {
|
|
|
|
- mInFlightRequests.remove(mCacheKey);
|
|
|
|
- }
|
|
|
|
- } else {
|
|
|
|
- // check to see if it is already batched for delivery.
|
|
|
|
- request = mBatchedResponses.get(mCacheKey);
|
|
|
|
- if (request != null) {
|
|
|
|
- request.removeContainerAndCancelIfNecessary(this);
|
|
|
|
- if (request.mContainers.size() == 0) {
|
|
|
|
- mBatchedResponses.remove(mCacheKey);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ private void throwIfNotOnMainThread() {
|
|
|
|
+ if (Looper.myLooper() != Looper.getMainLooper()) {
|
|
|
|
+ throw new IllegalStateException("ImageLoader must be invoked from the main thread.");
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
|
|
- /**
|
|
|
|
- * Returns the bitmap associated with the request URL if it has been loaded, null otherwise.
|
|
|
|
- */
|
|
|
|
- public Bitmap getBitmap() {
|
|
|
|
- return mBitmap;
|
|
|
|
- }
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Simple cache adapter interface. If provided to the ImageLoader, it will be used as an L1
|
|
|
|
+ * cache before dispatch to Volley. Implementations must not block. Implementation with an
|
|
|
|
+ * LruCache is recommended.
|
|
|
|
+ */
|
|
|
|
+ public interface ImageCache {
|
|
|
|
+ Bitmap getBitmap(String url);
|
|
|
|
+
|
|
|
|
+ void putBitmap(String url, Bitmap bitmap);
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Interface for the response handlers on image requests.
|
|
|
|
+ *
|
|
|
|
+ * <p>The call flow is this: 1. Upon being attached to a request, onResponse(response, true)
|
|
|
|
+ * will be invoked to reflect any cached data that was already available. If the data was
|
|
|
|
+ * available, response.getBitmap() will be non-null.
|
|
|
|
+ *
|
|
|
|
+ * <p>2. After a network response returns, only one of the following cases will happen: -
|
|
|
|
+ * onResponse(response, false) will be called if the image was loaded. or - onErrorResponse will
|
|
|
|
+ * be called if there was an error loading the image.
|
|
|
|
+ */
|
|
|
|
+ public interface ImageListener extends ErrorListener {
|
|
/**
|
|
/**
|
|
- * Returns the requested URL for this container.
|
|
|
|
|
|
+ * Listens for non-error changes to the loading of the image request.
|
|
|
|
+ *
|
|
|
|
+ * @param response Holds all information pertaining to the request, as well as the bitmap
|
|
|
|
+ * (if it is loaded).
|
|
|
|
+ * @param isImmediate True if this was called during ImageLoader.get() variants. This can be
|
|
|
|
+ * used to differentiate between a cached image loading and a network image loading in
|
|
|
|
+ * order to, for example, run an animation to fade in network loaded images.
|
|
*/
|
|
*/
|
|
- public String getRequestUrl() {
|
|
|
|
- return mRequestUrl;
|
|
|
|
- }
|
|
|
|
|
|
+ void onResponse(ImageContainer response, boolean isImmediate);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -443,22 +410,19 @@ public class ImageLoader {
|
|
* The request being tracked
|
|
* The request being tracked
|
|
*/
|
|
*/
|
|
private final Request<?> mRequest;
|
|
private final Request<?> mRequest;
|
|
-
|
|
|
|
|
|
+ /**
|
|
|
|
+ * List of all of the active ImageContainers that are interested in the request
|
|
|
|
+ */
|
|
|
|
+ private final List<ImageContainer> mContainers = new ArrayList<>();
|
|
/**
|
|
/**
|
|
* The result of the request being tracked by this item
|
|
* The result of the request being tracked by this item
|
|
*/
|
|
*/
|
|
private Bitmap mResponseBitmap;
|
|
private Bitmap mResponseBitmap;
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* Error if one occurred for this response
|
|
* Error if one occurred for this response
|
|
*/
|
|
*/
|
|
private VolleyError mError;
|
|
private VolleyError mError;
|
|
|
|
|
|
- /**
|
|
|
|
- * List of all of the active ImageContainers that are interested in the request
|
|
|
|
- */
|
|
|
|
- private final List<ImageContainer> mContainers = new ArrayList<>();
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* Constructs a new BatchedImageRequest object
|
|
* Constructs a new BatchedImageRequest object
|
|
*
|
|
*
|
|
@@ -471,17 +435,17 @@ public class ImageLoader {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Set the error for this response
|
|
|
|
|
|
+ * Get the error for this response
|
|
*/
|
|
*/
|
|
- public void setError(VolleyError error) {
|
|
|
|
- mError = error;
|
|
|
|
|
|
+ public VolleyError getError() {
|
|
|
|
+ return mError;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Get the error for this response
|
|
|
|
|
|
+ * Set the error for this response
|
|
*/
|
|
*/
|
|
- public VolleyError getError() {
|
|
|
|
- return mError;
|
|
|
|
|
|
+ public void setError(VolleyError error) {
|
|
|
|
+ mError = error;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -510,63 +474,77 @@ public class ImageLoader {
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Starts the runnable for batched delivery of responses if it is not already started.
|
|
|
|
- *
|
|
|
|
- * @param cacheKey The cacheKey of the response being delivered.
|
|
|
|
- * @param request The BatchedImageRequest to be delivered.
|
|
|
|
|
|
+ * Container object for all of the data surrounding an image request.
|
|
*/
|
|
*/
|
|
- private void batchResponse(String cacheKey, BatchedImageRequest request) {
|
|
|
|
- mBatchedResponses.put(cacheKey, request);
|
|
|
|
- // If we don't already have a batch delivery runnable in flight, make a new one.
|
|
|
|
- // Note that this will be used to deliver responses to all callers in mBatchedResponses.
|
|
|
|
- if (mRunnable == null) {
|
|
|
|
- mRunnable = new Runnable() {
|
|
|
|
- @Override
|
|
|
|
- public void run() {
|
|
|
|
- for (BatchedImageRequest bir : mBatchedResponses.values()) {
|
|
|
|
- for (ImageContainer container : bir.mContainers) {
|
|
|
|
- // If one of the callers in the batched request canceled the
|
|
|
|
- // request
|
|
|
|
- // after the response was received but before it was delivered,
|
|
|
|
- // skip them.
|
|
|
|
- if (container.mListener == null) {
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
- if (bir.getError() == null) {
|
|
|
|
- container.mBitmap = bir.mResponseBitmap;
|
|
|
|
- container.mListener.onResponse(container, false);
|
|
|
|
- } else {
|
|
|
|
- container.mListener.onErrorResponse(bir.getError());
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ public class ImageContainer {
|
|
|
|
+ private final ImageListener mListener;
|
|
|
|
+ /**
|
|
|
|
+ * The cache key that was associated with the request
|
|
|
|
+ */
|
|
|
|
+ private final String mCacheKey;
|
|
|
|
+ /**
|
|
|
|
+ * The request URL that was specified
|
|
|
|
+ */
|
|
|
|
+ private final String mRequestUrl;
|
|
|
|
+ /**
|
|
|
|
+ * The most relevant bitmap for the container. If the image was in cache, the Holder to use
|
|
|
|
+ * for the final bitmap (the one that pairs to the requested URL).
|
|
|
|
+ */
|
|
|
|
+ private Bitmap mBitmap;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Constructs a BitmapContainer object.
|
|
|
|
+ *
|
|
|
|
+ * @param bitmap The final bitmap (if it exists).
|
|
|
|
+ * @param requestUrl The requested URL for this container.
|
|
|
|
+ * @param cacheKey The cache key that identifies the requested URL for this container.
|
|
|
|
+ */
|
|
|
|
+ public ImageContainer(
|
|
|
|
+ Bitmap bitmap, String requestUrl, String cacheKey, ImageListener listener) {
|
|
|
|
+ mBitmap = bitmap;
|
|
|
|
+ mRequestUrl = requestUrl;
|
|
|
|
+ mCacheKey = cacheKey;
|
|
|
|
+ mListener = listener;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Releases interest in the in-flight request (and cancels it if no one else is listening).
|
|
|
|
+ */
|
|
|
|
+ public void cancelRequest() {
|
|
|
|
+ if (mListener == null) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ BatchedImageRequest request = mInFlightRequests.get(mCacheKey);
|
|
|
|
+ if (request != null) {
|
|
|
|
+ boolean canceled = request.removeContainerAndCancelIfNecessary(this);
|
|
|
|
+ if (canceled) {
|
|
|
|
+ mInFlightRequests.remove(mCacheKey);
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ // check to see if it is already batched for delivery.
|
|
|
|
+ request = mBatchedResponses.get(mCacheKey);
|
|
|
|
+ if (request != null) {
|
|
|
|
+ request.removeContainerAndCancelIfNecessary(this);
|
|
|
|
+ if (request.mContainers.size() == 0) {
|
|
|
|
+ mBatchedResponses.remove(mCacheKey);
|
|
}
|
|
}
|
|
- mBatchedResponses.clear();
|
|
|
|
- mRunnable = null;
|
|
|
|
}
|
|
}
|
|
- };
|
|
|
|
- // Post the runnable.
|
|
|
|
- mHandler.postDelayed(mRunnable, mBatchResponseDelayMs);
|
|
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- }
|
|
|
|
|
|
|
|
- /**
|
|
|
|
- * Creates a cache key for use with the L1 cache.
|
|
|
|
- *
|
|
|
|
- * @param url The URL of the request.
|
|
|
|
- * @param maxWidth The max-width of the output.
|
|
|
|
- * @param maxHeight The max-height of the output.
|
|
|
|
- * @param scaleType The scaleType of the imageView.
|
|
|
|
- */
|
|
|
|
- private static String getCacheKey(
|
|
|
|
- String url, int maxWidth, int maxHeight, ScaleType scaleType) {
|
|
|
|
- return new StringBuilder(url.length() + 12)
|
|
|
|
- .append("#W")
|
|
|
|
- .append(maxWidth)
|
|
|
|
- .append("#H")
|
|
|
|
- .append(maxHeight)
|
|
|
|
- .append("#S")
|
|
|
|
- .append(scaleType.ordinal())
|
|
|
|
- .append(url)
|
|
|
|
- .toString();
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Returns the bitmap associated with the request URL if it has been loaded, null otherwise.
|
|
|
|
+ */
|
|
|
|
+ public Bitmap getBitmap() {
|
|
|
|
+ return mBitmap;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Returns the requested URL for this container.
|
|
|
|
+ */
|
|
|
|
+ public String getRequestUrl() {
|
|
|
|
+ return mRequestUrl;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|