Universal-Image-Loader源码解析之线程管理
2017-09-13 本文已影响33人
LeonXtp
UIL中包括一下封装的任务:
- DisplayImageTask
- LoadAndDisplayImageTask
- ProcessAndDisplayImageTask
它们都在ImageLoadEngine
中得到统一调度执行。
线程需要关注的几个方面:
线程池类型
创建线程池的构造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
几种常见线程池.png
- 对于需要保证所有提交的任务都要被执行的情况,或者任务属于耗时长但是数量少的,使用FixedThreadPool
- 如果限定只能使用一个线程进行任务处理,使用SingleThreadExecutor
- 如果希望提交的任务尽快分配线程执行,或者说任务量大且每个任务耗时比较少的,使用CachedThreadPool
- 如果业务上允许任务执行失败,或者任务执行过程可能出现执行时间过长进而影响其他业务的应用场景,可以通过使用限定线程数量的线程池以及限定长度的队列进行容错处理。
UIL线程执行
ImageLoader
ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
options, listener, progressListener, engine.getLockForUri(uri));
LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
defineHandler(options));
if (options.isSyncLoading()) {
displayTask.run();
} else {
engine.submit(displayTask);
}
ImageLoadEngine
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);
}
}
});
}
- taskDistributor:主要任务就是根据磁盘是否有缓存,再来分配任务。由于在每创建一个新的线程的时候都需要读取一下磁盘,属于IO操作。需要图片缓存的应用一般在需要加载图片的时候,同时创建很多(>5)线程,这些线程一般来得猛去的也快,存活时间不必太长。
- taskExecutorForCachedImages:主要任务就是将磁盘上缓存的图片解码出来,并作后续处理,再显示
- taskExecutor:主要任务就是负责一个图片完整的从网络加载到磁盘缓存、内存缓存、预处理、后续处理、展示等。
它有如下几个主要方法:
private Executor createTaskExecutor() {
return DefaultConfigurationFactory
.createExecutor(configuration.threadPoolSize, configuration.threadPriority,
configuration.tasksProcessingType);
}
void pause() {
paused.set(true);
}
void resume() {
paused.set(false);
synchronized (pauseLock) {
pauseLock.notifyAll();
}
}
void stop() {
if (!configuration.customExecutor) {
((ExecutorService) taskExecutor).shutdownNow();
}
if (!configuration.customExecutorForCachedImages) {
((ExecutorService) taskExecutorForCachedImages).shutdownNow();
}
cacheKeysForImageAwares.clear();
uriLocks.clear();
}
其中shutdownNow()
方法会终止线程池中的执行
而这个pause()
和resume()
方法,在多图列表滑动时,可以通过调用此方法,暂停线程池中任务的执行以减轻cpu的负担,达到流畅滑动的目的。
在LoadAndDisplayImageTask
中,run()
方法执行之前,都会检查一下 waitIfPaused()
方法看此任务是否暂停,如果是,则先把线程锁锁上,然后进入等待。
private boolean waitIfPaused() {
AtomicBoolean pause = engine.getPause();
if (pause.get()) {
synchronized (engine.getPauseLock()) {
if (pause.get()) {
L.d(LOG_WAITING_FOR_RESUME, memoryCacheKey);
try {
engine.getPauseLock().wait();
} catch (InterruptedException e) {
L.e(LOG_TASK_INTERRUPTED, memoryCacheKey);
return true;
}
L.d(LOG_RESUME_AFTER_PAUSE, memoryCacheKey);
}
}
}
return isTaskNotActual();
}