Skip to content

Commit

Permalink
Avoided ImageLoader.getInstance() usages in the code (#158)
Browse files Browse the repository at this point in the history
Extracted ImageLoaderEngine from ImageLoader.
Changed API: PauseOnScrollListener requires ImageLoader instance in
constructor
  • Loading branch information
nostra13 committed Feb 2, 2013
1 parent 0d2ab70 commit 861ddfe
Show file tree
Hide file tree
Showing 5 changed files with 313 additions and 232 deletions.
Original file line number Diff line number Diff line change
@@ -1,59 +1,61 @@
package com.nostra13.universalimageloader.core;

import com.nostra13.universalimageloader.core.assist.ImageLoadingListener;
import com.nostra13.universalimageloader.core.display.BitmapDisplayer;
import com.nostra13.universalimageloader.utils.L;

import android.graphics.Bitmap;
import android.widget.ImageView;

/**
* Displays bitmap in {@link ImageView}. Must be called on UI thread.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @see ImageLoadingListener
* @see BitmapDisplayer
*/
final class DisplayBitmapTask implements Runnable {

private static final String LOG_DISPLAY_IMAGE_IN_IMAGEVIEW = "Display image in ImageView [%s]";
private static final String LOG_TASK_CANCELLED = "ImageView is reused for another image. Task is cancelled. [%s]";

private final Bitmap bitmap;
private final ImageView imageView;
private final String memoryCacheKey;
private final BitmapDisplayer bitmapDisplayer;
private final ImageLoadingListener listener;

private boolean loggingEnabled;

public DisplayBitmapTask(Bitmap bitmap, ImageLoadingInfo imageLoadingInfo) {
this.bitmap = bitmap;
imageView = imageLoadingInfo.imageView;
memoryCacheKey = imageLoadingInfo.memoryCacheKey;
bitmapDisplayer = imageLoadingInfo.options.getDisplayer();
listener = imageLoadingInfo.listener;
}

public void run() {
if (isViewWasReused()) {
if (loggingEnabled) L.i(LOG_TASK_CANCELLED, memoryCacheKey);
listener.onLoadingCancelled();
} else {
if (loggingEnabled) L.i(LOG_DISPLAY_IMAGE_IN_IMAGEVIEW, memoryCacheKey);
Bitmap displayedBitmap = bitmapDisplayer.display(bitmap, imageView);
listener.onLoadingComplete(displayedBitmap);
ImageLoader.getInstance().cancelDisplayTask(imageView);
}
}

/** Checks whether memory cache key (image URI) for current ImageView is actual */
private boolean isViewWasReused() {
String currentCacheKey = ImageLoader.getInstance().getLoadingUriForView(imageView);
return !memoryCacheKey.equals(currentCacheKey);
}

void setLoggingEnabled(boolean loggingEnabled) {
this.loggingEnabled = loggingEnabled;
}
}
package com.nostra13.universalimageloader.core;

import com.nostra13.universalimageloader.core.assist.ImageLoadingListener;
import com.nostra13.universalimageloader.core.display.BitmapDisplayer;
import com.nostra13.universalimageloader.utils.L;

import android.graphics.Bitmap;
import android.widget.ImageView;

/**
* Displays bitmap in {@link ImageView}. Must be called on UI thread.
*
* @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
* @see ImageLoadingListener
* @see BitmapDisplayer
*/
final class DisplayBitmapTask implements Runnable {

private static final String LOG_DISPLAY_IMAGE_IN_IMAGEVIEW = "Display image in ImageView [%s]";
private static final String LOG_TASK_CANCELLED = "ImageView is reused for another image. Task is cancelled. [%s]";

private final Bitmap bitmap;
private final ImageView imageView;
private final String memoryCacheKey;
private final BitmapDisplayer bitmapDisplayer;
private final ImageLoadingListener listener;
private final ImageLoaderEngine engine;

private boolean loggingEnabled;

public DisplayBitmapTask(Bitmap bitmap, ImageLoadingInfo imageLoadingInfo, ImageLoaderEngine engine) {
this.bitmap = bitmap;
imageView = imageLoadingInfo.imageView;
memoryCacheKey = imageLoadingInfo.memoryCacheKey;
bitmapDisplayer = imageLoadingInfo.options.getDisplayer();
listener = imageLoadingInfo.listener;
this.engine = engine;
}

public void run() {
if (isViewWasReused()) {
if (loggingEnabled) L.i(LOG_TASK_CANCELLED, memoryCacheKey);
listener.onLoadingCancelled();
} else {
if (loggingEnabled) L.i(LOG_DISPLAY_IMAGE_IN_IMAGEVIEW, memoryCacheKey);
Bitmap displayedBitmap = bitmapDisplayer.display(bitmap, imageView);
listener.onLoadingComplete(displayedBitmap);
engine.cancelDisplayTaskFor(imageView);
}
}

/** Checks whether memory cache key (image URI) for current ImageView is actual */
private boolean isViewWasReused() {
String currentCacheKey = engine.getLoadingUriForView(imageView);
return !memoryCacheKey.equals(currentCacheKey);
}

void setLoggingEnabled(boolean loggingEnabled) {
this.loggingEnabled = loggingEnabled;
}
}
104 changes: 15 additions & 89 deletions library/src/com/nostra13/universalimageloader/core/ImageLoader.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,6 @@
package com.nostra13.universalimageloader.core;

import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;

import android.content.Context;
import android.graphics.Bitmap;
Expand All @@ -27,9 +15,7 @@
import com.nostra13.universalimageloader.core.assist.ImageLoadingListener;
import com.nostra13.universalimageloader.core.assist.ImageSize;
import com.nostra13.universalimageloader.core.assist.MemoryCacheUtil;
import com.nostra13.universalimageloader.core.assist.QueueProcessingType;
import com.nostra13.universalimageloader.core.assist.SimpleImageLoadingListener;
import com.nostra13.universalimageloader.core.assist.deque.LIFOLinkedBlockingDeque;
import com.nostra13.universalimageloader.core.display.BitmapDisplayer;
import com.nostra13.universalimageloader.core.display.FakeBitmapDisplayer;
import com.nostra13.universalimageloader.utils.L;
Expand All @@ -50,17 +36,11 @@ public class ImageLoader {
private static final String LOG_LOAD_IMAGE_FROM_MEMORY_CACHE = "Load image from memory cache [%s]";

private ImageLoaderConfiguration configuration;
private ExecutorService imageLoadingExecutor;
private ExecutorService cachedImageLoadingExecutor;
private ExecutorService taskDistributor;
private ImageLoaderEngine engine;

private final ImageLoadingListener emptyListener = new SimpleImageLoadingListener();
private final BitmapDisplayer fakeBitmapDisplayer = new FakeBitmapDisplayer();

private final Map<Integer, String> cacheKeysForImageViews = Collections.synchronizedMap(new HashMap<Integer, String>());
private final Map<String, ReentrantLock> uriLocks = new WeakHashMap<String, ReentrantLock>();
private final AtomicBoolean paused = new AtomicBoolean(false);

private volatile static ImageLoader instance;

/** Returns singleton class instance */
Expand Down Expand Up @@ -90,13 +70,14 @@ public synchronized void init(ImageLoaderConfiguration configuration) {
throw new IllegalArgumentException(ERROR_INIT_CONFIG_WITH_NULL);
}
if (this.configuration == null) {
engine = new ImageLoaderEngine(configuration);
this.configuration = configuration;
}
}

/**
* Returns true - if ImageLoader {@linkplain #init(ImageLoaderConfiguration) is initialized with configuration};
* false - otherwise
* Returns <b>true</b> - if ImageLoader {@linkplain #init(ImageLoaderConfiguration) is initialized with
* configuration}; <b>false</b> - otherwise
*/
public boolean isInited() {
return configuration != null;
Expand Down Expand Up @@ -170,7 +151,7 @@ public void displayImage(String uri, ImageView imageView, ImageLoadingListener l
* @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before
* @throws IllegalArgumentException if passed <b>imageView</b> is null
*/
public void displayImage(final String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener) {
public void displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener) {
checkConfiguration();
if (imageView == null) {
throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);
Expand All @@ -183,7 +164,7 @@ public void displayImage(final String uri, ImageView imageView, DisplayImageOpti
}

if (uri == null || uri.length() == 0) {
cacheKeysForImageViews.remove(imageView.hashCode());
engine.cancelDisplayTaskFor(imageView);
listener.onLoadingStarted();
if (options.isShowImageForEmptyUri()) {
imageView.setImageResource(options.getImageForEmptyUri());
Expand All @@ -196,7 +177,7 @@ public void displayImage(final String uri, ImageView imageView, DisplayImageOpti

ImageSize targetSize = getImageSizeScaleTo(imageView);
String memoryCacheKey = MemoryCacheUtil.generateKey(uri, targetSize);
cacheKeysForImageViews.put(imageView.hashCode(), memoryCacheKey);
engine.prepareDisplayTaskFor(imageView, memoryCacheKey);

Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
if (bmp != null && !bmp.isRecycled()) {
Expand All @@ -215,21 +196,9 @@ public void displayImage(final String uri, ImageView imageView, DisplayImageOpti
}
}

initExecutorsIfNeed();
ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, options, listener, getLockForUri(uri));
final LoadAndDisplayImageTask displayImageTask = new LoadAndDisplayImageTask(configuration, imageLoadingInfo, new Handler());

taskDistributor.submit(new Runnable() {
@Override
public void run() {
boolean isImageCachedOnDisc = configuration.discCache.get(uri).exists();
if (isImageCachedOnDisc) {
cachedImageLoadingExecutor.submit(displayImageTask);
} else {
imageLoadingExecutor.submit(displayImageTask);
}
}
});
ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, options, listener, engine.getLockForUri(uri));
final LoadAndDisplayImageTask displayImageTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo, new Handler());
engine.submit(displayImageTask);
}
}

Expand Down Expand Up @@ -345,25 +314,6 @@ private void checkConfiguration() {
}
}

private void initExecutorsIfNeed() {
if (imageLoadingExecutor == null || imageLoadingExecutor.isShutdown()) {
imageLoadingExecutor = createTaskExecutor();
}
if (cachedImageLoadingExecutor == null || cachedImageLoadingExecutor.isShutdown()) {
cachedImageLoadingExecutor = createTaskExecutor();
}
if (taskDistributor == null || taskDistributor.isShutdown()) {
taskDistributor = Executors.newCachedThreadPool();
}
}

private ExecutorService createTaskExecutor() {
boolean lifo = configuration.tasksProcessingType == QueueProcessingType.LIFO;
BlockingQueue<Runnable> taskQueue = lifo ? new LIFOLinkedBlockingDeque<Runnable>() : new LinkedBlockingQueue<Runnable>();
return new ThreadPoolExecutor(configuration.threadPoolSize, configuration.threadPoolSize, 0L, TimeUnit.MILLISECONDS, taskQueue,
configuration.displayImageThreadFactory);
}

/**
* Returns memory cache
*
Expand Down Expand Up @@ -406,7 +356,7 @@ public void clearDiscCache() {

/** Returns URI of image which is loading at this moment into passed {@link ImageView} */
public String getLoadingUriForView(ImageView imageView) {
return cacheKeysForImageViews.get(imageView.hashCode());
return engine.getLoadingUriForView(imageView);
}

/**
Expand All @@ -415,36 +365,25 @@ public String getLoadingUriForView(ImageView imageView) {
* @param imageView {@link ImageView} for which display task will be cancelled
*/
public void cancelDisplayTask(ImageView imageView) {
cacheKeysForImageViews.remove(imageView.hashCode());
engine.cancelDisplayTaskFor(imageView);
}

/**
* Pause ImageLoader. All new "load&display" tasks won't be executed until ImageLoader is {@link #resume() resumed}.<br />
* Already running tasks are not paused.
*/
public void pause() {
paused.set(true);
engine.pause();
}

/** Resumes waiting "load&display" tasks */
public void resume() {
synchronized (paused) {
paused.set(false);
paused.notifyAll();
}
engine.resume();
}

/** Stops all running display image tasks, discards all other scheduled tasks */
public void stop() {
if (imageLoadingExecutor != null) {
imageLoadingExecutor.shutdownNow();
}
if (cachedImageLoadingExecutor != null) {
cachedImageLoadingExecutor.shutdownNow();
}
if (taskDistributor != null) {
taskDistributor.shutdownNow();
}
engine.stop();
}

/**
Expand Down Expand Up @@ -487,17 +426,4 @@ private int getFieldValue(Object object, String fieldName) {
}
return value;
}

private ReentrantLock getLockForUri(String uri) {
ReentrantLock lock = uriLocks.get(uri);
if (lock == null) {
lock = new ReentrantLock();
uriLocks.put(uri, lock);
}
return lock;
}

AtomicBoolean getPause() {
return paused;
}
}
Loading

0 comments on commit 861ddfe

Please sign in to comment.