diff --git a/src/main/java/com/android/volley/toolbox/NetworkImageView.java b/src/main/java/com/android/volley/toolbox/NetworkImageView.java index ee248103..d07401e7 100644 --- a/src/main/java/com/android/volley/toolbox/NetworkImageView.java +++ b/src/main/java/com/android/volley/toolbox/NetworkImageView.java @@ -48,6 +48,9 @@ public class NetworkImageView extends ImageView { /** Current ImageContainer. (either in-flight or finished) */ private ImageContainer mImageContainer; + + // fix synchronization issue + private Object dummyToSynchronize = new Object(); public NetworkImageView(Context context) { this(context, null); @@ -110,89 +113,96 @@ public void setErrorImageResId(int errorImage) { * Loads the image for the view if it isn't already loaded. * @param isInLayoutPass True if this was invoked from a layout pass, false otherwise. */ - void loadImageIfNecessary(final boolean isInLayoutPass) { - int width = getWidth(); - int height = getHeight(); - - boolean wrapWidth = false, wrapHeight = false; - if (getLayoutParams() != null) { - wrapWidth = getLayoutParams().width == LayoutParams.WRAP_CONTENT; - wrapHeight = getLayoutParams().height == LayoutParams.WRAP_CONTENT; - } - - // if the view's bounds aren't known yet, and this is not a wrap-content/wrap-content - // view, hold off on loading the image. - boolean isFullyWrapContent = wrapWidth && wrapHeight; - if (width == 0 && height == 0 && !isFullyWrapContent) { - return; - } - - // if the URL to be loaded in this view is empty, cancel any old requests and clear the - // currently loaded image. - if (TextUtils.isEmpty(mUrl)) { - if (mImageContainer != null) { - mImageContainer.cancelRequest(); - mImageContainer = null; - } - setDefaultImageOrNull(); - return; - } - - // if there was an old request in this view, check if it needs to be canceled. - if (mImageContainer != null && mImageContainer.getRequestUrl() != null) { - if (mImageContainer.getRequestUrl().equals(mUrl)) { - // if the request is from the same URL, return. - return; - } else { - // if there is a pre-existing request, cancel it if it's fetching a different URL. - mImageContainer.cancelRequest(); - setDefaultImageOrNull(); - } - } - - // Calculate the max image width / height to use while ignoring WRAP_CONTENT dimens. - int maxWidth = wrapWidth ? 0 : width; - int maxHeight = wrapHeight ? 0 : height; - - // The pre-existing content of this view didn't match the current URL. Load the new image - // from the network. - ImageContainer newContainer = mImageLoader.get(mUrl, - new ImageListener() { - @Override - public void onErrorResponse(VolleyError error) { - if (mErrorImageId != 0) { - setImageResource(mErrorImageId); - } - } - - @Override - public void onResponse(final ImageContainer response, boolean isImmediate) { - // If this was an immediate response that was delivered inside of a layout - // pass do not set the image immediately as it will trigger a requestLayout - // inside of a layout. Instead, defer setting the image by posting back to - // the main thread. - if (isImmediate && isInLayoutPass) { - post(new Runnable() { - @Override - public void run() { - onResponse(response, false); - } - }); - return; - } - - if (response.getBitmap() != null) { - setImageBitmap(response.getBitmap()); - } else if (mDefaultImageId != 0) { - setImageResource(mDefaultImageId); - } - } - }, maxWidth, maxHeight); - - // update the ImageContainer to be the new bitmap container. - mImageContainer = newContainer; - } - + private void loadImageIfNecessary(final boolean isInLayoutPass) { + int width = getWidth(); + int height = getHeight(); + + boolean isFullyWrapContent = getLayoutParams() != null + && getLayoutParams().height == LayoutParams.WRAP_CONTENT + && getLayoutParams().width == LayoutParams.WRAP_CONTENT; + // if the view's bounds aren't known yet, and this is not a + // wrap-content/wrap-content + // view, hold off on loading the image. + if (width == 0 && height == 0 && !isFullyWrapContent) { + return; + } + synchronized (dummyToSynchronize) { + + // if the URL to be loaded in this view is empty, cancel any old + // requests and clear the + // currently loaded image. + + if (TextUtils.isEmpty(mUrl)) { + if (mImageContainer != null) { + mImageContainer.cancelRequest(); + mImageContainer = null; + } + setDefaultImageOrNull(); + return; + } + + // if there was an old request in this view, check if it needs to be + // canceled. + if (mImageContainer != null + && mImageContainer.getRequestUrl() != null) { + if (mImageContainer.getRequestUrl().equals(mUrl)) { + // if the request is from the same URL, return. + return; + } else { + // if there is a pre-existing request, cancel it if it's + // fetching a different URL. + mImageContainer.cancelRequest(); + setDefaultImageOrNull(); + } + } + } + + // The pre-existing content of this view didn't match the current URL. + // Load the new image + // from the network. + ImageContainer newContainer = mImageLoader.get(mUrl, + new ImageListener() { + @Override + public void onErrorResponse(VolleyError error) { + if (mErrorImageId != 0) { + setImageResource(mErrorImageId); + } + } + + @Override + public void onResponse(final ImageContainer response, + boolean isImmediate) { + // If this was an immediate response that was delivered + // inside of a layout + // pass do not set the image immediately as it will + // trigger a requestLayout + // inside of a layout. Instead, defer setting the image + // by posting back to + // the main thread. + if (isImmediate && isInLayoutPass) { + post(new Runnable() { + @Override + public void run() { + onResponse(response, false); + } + }); + return; + } + synchronized (dummyToSynchronize) { + if (response == mImageContainer) + if (response.getBitmap() != null) { + setImageBitmap(response.getBitmap()); + } else if (mDefaultImageId != 0) { + setImageResource(mDefaultImageId); + } + } + } + }); + + // update the ImageContainer to be the new bitmap container. + mImageContainer = newContainer; + } + private void setDefaultImageOrNull() { if(mDefaultImageId != 0) { setImageResource(mDefaultImageId);