在介绍了一些基本组件后,我们越来越接近核心逻辑,任务调度设计一些数据结构,这些数据结构决定了任务如何调度。
在开始之前,笔者建议如果读者还不知道ReentrantLock Condition如何使用,建议先查一下,否则理解线程如何调度有些困难
双端队列:既可以当栈使用,又可以当做队列使用, LIFO LIFO两种
框架使用的接口
/** * Insert(插入操作) * addFirst(e) * offerFirst(e) * addLast(e) * offerLast(e) * Remove(删除操作) * removeFirst() * pollFirst() * removeLast() * pollLast() * Examine(只查看不删除) * getFirst() * peekFirst() * getLast() * peekLast() * * * 当做队列使用 Queue Method * 等价方法 Deque Method * java.util.Queue.add(e) * addLast(e) * java.util.Queue.offer(e) * offerLast(e) * java.util.Queue.remove() * removeFirst() * java.util.Queue.poll() * pollFirst() * java.util.Queue.element() * getFirst() * java.util.Queue.peek() * peekFirst() * * 当做栈使用 Stack Method * 等价方法 Deque Method * push(e) * addFirst(e) * * pop() * removeFirst() * peek() * peekFirst() */ public interface Deque<E> extends Queue<E> { void addFirst(E e); void addLast(E e); boolean offerFirst(E e); boolean offerLast(E e); E removeFirst(); E removeLast(); E pollFirst(); E pollLast(); E getFirst(); E getLast(); E peekFirst(); E peekLast(); boolean removeFirstOccurrence(Object o); boolean removeLastOccurrence(Object o); // *** Queue methods *** boolean add(E e); boolean offer(E e); E remove(); E poll(); E element(); E peek(); // *** Stack methods *** void push(E e); E pop(); // *** Collection methods *** boolean remove(Object o); boolean contains(Object o); public int size(); Iterator<E> iterator(); Iterator<E> descendingIterator(); }
为了完成任务调度,还需要一个插入删除操作线程阻塞的队列BlockingDequeue,在这里就不介绍接口方法了,我们把目光转向具体实现:
LinkedBlockingDeque ,用两个条件变量来控制插入和删除的阻塞动作,lock锁为当前队列的对象
/** Condition for waiting takes */ private final Condition notEmpty = lock.newCondition(); /** Condition for waiting puts */ private final Condition notFull = lock.newCondition();
/** * Links node as last element, or returns false if full. */ private boolean linkLast(Node<E> node) { // assert lock.isHeldByCurrentThread(); if (count >= capacity) return false; Node<E> l = last; node.prev = l; last = node; if (first == null) first = node; else l.next = node; ++count; notEmpty.signal();//线程同步操作,这样因为没有数据要处理的线程便可以继续处理了 return true; }
public void putLast(E e) throws InterruptedException { if (e == null) throw new NullPointerException(); Node<E> node = new Node<E>(e); final ReentrantLock lock = this.lock; lock.lock(); try { while (!linkLast(node)) notFull.await();//满了就等待,直到notFull.notify()被调用 } finally { lock.unlock(); } }
LIFOLinkedBlockingDeque 的实现逻辑几乎和LinkedBlockingDeque一样,只是颠倒了一下插入删除的顺序
总之,blocking的含义就是,满了就等待处理,空了也等待数据
有了这个双端队列,那么任务是如何被调度的呢?
到最核心的包com.nostra13.universalimageloader.core 下,找到 DefaultConfigurationFactory类,这个类为我们提供了一系列静态方法,来创建默认的配置参数,其中有这样一个方法:
/** Creates default implementation of task executor */ public static Executor createExecutor(int threadPoolSize, int threadPriority, QueueProcessingType tasksProcessingType) { boolean lifo = tasksProcessingType == QueueProcessingType.LIFO; BlockingQueue<Runnable> taskQueue = lifo ? new LIFOLinkedBlockingDeque<Runnable>() : new LinkedBlockingQueue<Runnable>(); return new ThreadPoolExecutor(threadPoolSize, threadPoolSize, 0L, TimeUnit.MILLISECONDS, taskQueue, createThreadFactory(threadPriority, "uil-pool-")); }
/** Creates default implementation of task distributor */ public static Executor createTaskDistributor() { return Executors.newCachedThreadPool(createThreadFactory(Thread.NORM_PRIORITY, "uil-pool-d-")); }
UniversalImageLoader加载图片分为任务分发 和 任务执行 两个步骤来进行,读者先有个概念,我们先来看一看Runnable的具体实现是什么
LoadAndDisplayImageTask ProcessAndDisplayImageTask DisplayBitmapTask 是Runnable的具体实现 ,最终的图片显示都会由DisplayBitmapTask 来做。
LoadAndDisplayImageTask 负责下载后将图片显示出来
ProcessAndDisplayImageTask 仅仅是在图片被显示之前做了一下处理后,流程就转给LoadAndDisplayImageTask处理
LoadAndDisplayImageTask的核心方法是
static void runTask(Runnable r, boolean sync, Handler handler, ImageLoaderEngine engine) { if (sync) { r.run(); } else if (handler == null) { engine.fireCallback(r); } else { handler.post(r); } }我们先不管engine是什么,只要知道它是用来执行任务的即可
另一个核心方法就是:
@Override public void run() { if (waitIfPaused()) return; if (delayIfNeed()) return; ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock; L.d(LOG_START_DISPLAY_IMAGE_TASK, memoryCacheKey); if (loadFromUriLock.isLocked()) { L.d(LOG_WAITING_FOR_IMAGE_LOADED, memoryCacheKey); } loadFromUriLock.lock(); Bitmap bmp; try { checkTaskNotActual(); bmp = configuration.memoryCache.get(memoryCacheKey);//先去内存里找 if (bmp == null || bmp.isRecycled()) { bmp = tryLoadBitmap();//内存找不到就去磁盘上看看,磁盘没有就到网上去下载 if (bmp == null) return; // 都没有就返回空 checkTaskNotActual(); checkTaskInterrupted(); if (options.shouldPreProcess()) { L.d(LOG_PREPROCESS_IMAGE, memoryCacheKey); bmp = options.getPreProcessor().process(bmp); if (bmp == null) { L.e(ERROR_PRE_PROCESSOR_NULL, memoryCacheKey); } } if (bmp != null && options.isCacheInMemory()) { L.d(LOG_CACHE_IMAGE_IN_MEMORY, memoryCacheKey); configuration.memoryCache.put(memoryCacheKey, bmp); } } else { loadedFrom = LoadedFrom.MEMORY_CACHE; L.d(LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING, memoryCacheKey); } if (bmp != null && options.shouldPostProcess()) { L.d(LOG_POSTPROCESS_IMAGE, memoryCacheKey); bmp = options.getPostProcessor().process(bmp); if (bmp == null) { L.e(ERROR_POST_PROCESSOR_NULL, memoryCacheKey); } } checkTaskNotActual(); checkTaskInterrupted(); } catch (TaskCancelledException e) { fireCancelEvent(); return; } finally { loadFromUriLock.unlock(); } //代码走道这里,这个bmp应该是不为null的 DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);//执行显示图片的任务 runTask(displayBitmapTask, syncLoading, handler, engine); }
最后,也是最重量级的一个组件ImageLoaderEngine,先看构造函数:
ImageLoaderEngine(ImageLoaderConfiguration configuration) { this.configuration = configuration;//参数配置类,保存我们的配置参数,后面会讲解 taskExecutor = configuration.taskExecutor;//任务执行 taskExecutorForCachedImages = configuration.taskExecutorForCachedImages;//任务执行,针对已缓存图片 taskDistributor = DefaultConfigurationFactory.createTaskDistributor();//任务分发 }
void fireCallback(Runnable r) { taskDistributor.execute(r); }
/** Submits task to execution pool */ void submit(final LoadAndDisplayImageTask task) { taskDistributor.execute(new Runnable() { @Override public void run() { File image = configuration.diskCache.get(task.getLoadingUri()); boolean isImageCachedOnDisk = image != null && image.exists(); initExecutorsIfNeed(); if (isImageCachedOnDisk) { taskExecutorForCachedImages.execute(task);//执行加载缓存图片的任务 } else { taskExecutor.execute(task);//执行下载并缓存的任务 } } }); }
/** Submits task to execution pool */ void submit(ProcessAndDisplayImageTask task) { initExecutorsIfNeed(); taskExecutorForCachedImages.execute(task); }
先小小总结一下:Engine用了3个Executor,一个负责分发,另两个负责显示图片(一个负责从缓存里加载,一个负责从网上加载并缓存),而我们的BlockingQueue,在Executor初始化的时候作为参数传了进去。
是时候开始介绍最后三个应用接口类了,我想你一定不陌生:ImageLoader ImageLoaderConfiguration DisplayImageOptions
为了使用ImageLoader,你需要对ImageLoader进行初始化,大概是这样的
private synchronized void initImageLoaderConfig(){ ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this) .threadPoolSize(3)//线程池内加载的数量 .threadPriority(Thread.NORM_PRIORITY - 1) .memoryCache(new FIFOLimitedMemoryCache(720 * 1280 * 8)) .tasksProcessingOrder(QueueProcessingType.LIFO) .imageDownloader(new FDSVideoDownloader(this.activity(), 15000, 30000)) .build(); ImageLoader.getInstance().init(config); }
private void initImageDisplayOptions(Context context){ options = new DisplayImageOptions.Builder() .showImageOnLoading(context.getResources().getDrawable(R.drawable.load_fail)) .showImageForEmptyUri(context.getResources().getDrawable(R.drawable.load_fail)) .showImageOnFail(context.getResources().getDrawable(R.drawable.load_fail)) .cacheInMemory(true) .cacheOnDisk(true) .considerExifParams(true) .bitmapConfig(Bitmap.Config.RGB_565) .imageScaleType(ImageScaleType.EXACTLY_STRETCHED) .build(); }
ImageLoader.getInstance().displayImage( "http://www.example_imageuri, mHolder.mImage, options, null);
最后,当你不需要ImageLoader时,别忘了退出软件的时候,在activity回调方法里
@Override public void onDestroy() { super.onDestroy(); ImageLoader.getInstance().destroy(); }
好了,知道了基本的使用后,我们看一下displayImage这个方法
public void displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener) { displayImage(uri, imageView, options, listener, null); }
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) { checkConfiguration(); if (imageAware == null) { throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS); } if (listener == null) { listener = emptyListener; } if (options == null) { options = configuration.defaultDisplayImageOptions; } if (TextUtils.isEmpty(uri)) { engine.cancelDisplayTaskFor(imageAware); listener.onLoadingStarted(uri, imageAware.getWrappedView()); if (options.shouldShowImageForEmptyUri()) { imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources)); } else { imageAware.setImageDrawable(null); } listener.onLoadingComplete(uri, imageAware.getWrappedView(), null); return; } ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize()); String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize); engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);//仅仅缓存了key而已,方便查找 listener.onLoadingStarted(uri, imageAware.getWrappedView()); Bitmap bmp = configuration.memoryCache.get(memoryCacheKey); if (bmp != null && !bmp.isRecycled()) { L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey); if (options.shouldPostProcess()) { ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, engine.getLockForUri(uri)); ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo, defineHandler(options));//new 了一个task if (options.isSyncLoading()) { displayTask.run(); } else { engine.submit(displayTask);//提交给Executor去处理 } } else { options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE); listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp); } } else { if (options.shouldShowImageOnLoading()) { imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources)); } else if (options.isResetViewBeforeLoading()) { imageAware.setImageDrawable(null); } ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, engine.getLockForUri(uri)); LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo, defineHandler(options));//new 了一个task if (options.isSyncLoading()) { displayTask.run(); } else { engine.submit(displayTask);//提交给Executor去处理 } } }
至此,你应该知道了整个UIL的工作流程,本人分析的比较详细,只是为了避繁从简,能让大家马上抓住UIL的主干,所以采取了这种方式。如果有哪位博友对这里面的问题不是很清楚,可以给我发邮件,或者给我留言,源码分析并没有完成,下一篇文章主要讲一下有关内存泄漏的问题,是的,如果你使用不当,UIL会造成Activity泄漏,后果是致命的