Picasso源码完全解析(七)-- CleanupThread 取消请求
2017-05-17 11:37
846 查看
Picasso源码完全解析(二)--Picasso实例的创建
Picasso源码完全解析(三)--Request和Action的创建
Picasso源码完全解析(四)--Action分发和执行
Picasso源码完全解析(五)--图片的获取(BitmapHunter)
Picasso源码完全解析(六)--请求的取消、暂停、和恢复
Picasso源码完全解析(七)--
CleanupThread 取消请求
Picasso源码完全解析(七)-- CleanupThread 取消请求
在开发过程中,我们常常有这样一种应用场景:在使用ListView或者RecylerView或者GridView时,为了提高性能,会采用item复用机制,即默认加载一屏的item,当item变得不可用的时候用来显示下一个将要显示的item,这样避免重复创建和销毁item。如果item用来显示图片,那么会造成这样一个问题:由于图片加载是一个耗时的异步过程,这样会导致当一个item被复用来显示另一个item的时候,一个item便会对应多个图片加载请求,由于异步过程的不确定性,多个请求可能会无序的到达,导致item的显示错乱问题。为了避免这个问题,常用的做法是给这个item设置一个tag,当请求结果返回的时候,对比tag,如果相同就显示,如果不同就舍弃。在Piscasso里,自动帮我们做了这样一件事情,因此无需考虑这个问题。这是通过ClearupThread来实现的。下面就此进行详细分析。先来看看CleanupThread源码:
private static class CleanupThread extends Thread { private final ReferenceQueue<Object> referenceQueue; private final Handler handler; CleanupThread(ReferenceQueue<Object> referenceQueue, Handler handler) { this.referenceQueue = referenceQueue; this.handler = handler; setDaemon(true); setName(THREAD_PREFIX + "refQueue"); } @Override public void run() { Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND); while (true) { try { // Prior to Android 5.0, even when there is no local variable, the result from // remove() & obtainMessage() is kept as a stack local variable. // We're forcing this reference to be cleared and replaced by looping every second // when there is nothing to do. // This behavior has been tested and reproduced with heap dumps. RequestWeakReference<?> remove = (RequestWeakReference<?>) referenceQueue.remove(THREAD_LEAK_CLEANING_MS); Message message = handler.obtainMessage(); if (remove != null) { message.what = REQUEST_GCED; message.obj = remove.action; handler.sendMessage(message); } else { message.recycle(); } } catch (InterruptedException e) { break; } catch (final Exception e) { handler.post(new Runnable() { @Override public void run() { throw new RuntimeException(e); } }); break; } } } void shutdown() { interrupt(); } }
可以看到CleanupThread是一个线程,持有一个ReferenceQueue和Handler,在创建的时候通过setDaemon(true)守护线程。在run()方法里,通过一个死循环,不停的从referenceQueue里移出入队的引用对象(RequestWeakReference),并通过Handler发消息进行处理。
那么CleanupThread是什么时候初始化的呢?
在Picasso的构造函数里可以看到:
this.referenceQueue = new ReferenceQueue<>(); this.cleanupThread = new CleanupThread(referenceQueue, HANDLER); this.cleanupThread.start();
CleanupThread正是在Picasso的构造函数里初始化的。
其初始化的Handler是picasso的static Handler。检查这个Handler的实现发现,该Handler里的操作必须执行在主线程中,因此,Picasso的类加载必须在主线程的执行环境里,也就是第一次对picasso的调用必须是在主线程里。
在Picasso里,持有一个ReferenceQueue,并用它来初始化一个ClearupThread,ReferenceQueue本身是用来注册应用对象的,用来和垃圾回收机制进行简单通讯,即:在创建引用对象的时候,为其注册一个ReferenceQueue, 我们知道,java应用分为强引用、软应用、弱引用、虚引用。区别是,一个对象被强引用引用的时候,是无法被垃圾回收机制回收的,而其他引用可以在gc的时候在一定条件下被垃圾回收机制回收。当该reference对象的referent被垃圾回收机制回收的时候,该引用对象会被加入到与之关联的ReferenceQueue。
那么,与picasso的ReferenceQueue关联的是什么引用呢?
Action(Picasso picasso, T target, Request request, int memoryPolicy, int networkPolicy, int errorResId, Drawable errorDrawable, String key, Object tag, boolean noFade) { this.picasso = picasso; this.request = request; this.target = target == null ? null : new RequestWeakReference<>(this, target, picasso.referenceQueue); this.memoryPolicy = memoryPolicy; this.networkPolicy = networkPolicy; this.noFade = noFade; this.errorResId = errorResId; this.errorDrawable = errorDrawable; this.key = key; this.tag = (tag != null ? tag : this); }
可以看到,与之关联的是一个RequestWeakReference,是一个弱引用。
接着看看RequestWeakReference的源码:
static class RequestWeakReference<M> extends WeakReference<M> { final Action action; public RequestWeakReference(Action action, M referent, ReferenceQueue<? super M> q) { super(referent, q); this.action = action; } }
可以看到,这个RequestWeakReference的referent是Action构造函数的target参数。
Action是一个抽象类,其子类有:FetchAction、GetAction、ImageViewAction、RemoteViewsAction、TargetAction。看下这些类的构造:
TargetAction(Picasso picasso, Target target, Request data, int memoryPolicy, int networkPolicy, Drawable errorDrawable, String key, Object tag, int errorResId) { super(picasso, target, data, memoryPolicy, networkPolicy, errorResId, errorDrawable, key, tag, false); } RemoteViewsAction(Picasso picasso, Request data, RemoteViews remoteViews, int viewId, int errorResId, int memoryPolicy, int networkPolicy, Object tag, String key, Callback callback) { super(picasso, null, data, memoryPolicy, networkPolicy, errorResId, null, key, tag, false); this.remoteViews = remoteViews; this.viewId = viewId; this.callback = callback; } ImageViewAction(Picasso picasso, ImageView imageView, Request data, int memoryPolicy, int networkPolicy, int errorResId, Drawable errorDrawable, String key, Object tag, Callback callback, boolean noFade) { super(picasso, imageView, data, memoryPolicy, networkPolicy, errorResId, errorDrawable, key, tag, noFade); this.callback = callback; } GetAction(Picasso picasso, Request data, int memoryPolicy, int networkPolicy, Object tag, String key) { super(picasso, null, data, memoryPolicy, networkPolicy, 0, null, key, tag, false); } FetchAction(Picasso picasso, Request data, int memoryPolicy, int networkPolicy, Object tag, String key, Callback callback) { super(picasso, null, data, memoryPolicy, networkPolicy, 0, null, key, tag, false); this.target = new Object(); this.callback = callback; }
可以看到只有ImageViewAction和TargetAction的target有值,分别对应一个ImageView和Target对象,其他的都为空。
当target被回收的时候,ClearupThread能够收到通知,会通过Handler发送一个消息,看看这个消息是如何处理的,
case REQUEST_GCED: { Action action = (Action) msg.obj; if (action.getPicasso().loggingEnabled) { log(OWNER_MAIN, VERB_CANCELED, action.request.logId(), "target got garbage collected"); } action.picasso.cancelExistingRequest(action.getTarget()); break; }
可以清楚的看到,当Action的target被回收的时候,picasso会去取消其对应的Action
综上所述:当一个Action的Target弱可达的但是其Request没有被取消的时候,它会被加入到CleanupThread的ReferenceQueue里,CleanupThread从队列里将它清除,并取消它对应的请求。
相关文章推荐
- Picasso源码完全解析(六)--请求的取消、暂停、和恢复
- OkHttp框架的RetryAndFollowUpInterceptor请求重定向源码解析
- Picasso源码完全解析(四)--Action分发和执行
- Android 图片加载框架Picasso基本使用和源码完全解析
- 深入Android HandlerThread 使用及其源码完全解析
- Picasso源码完全解析(一)--概述
- Picasso源码完全解析(二)--Picasso实例的创建
- Picasso源码完全解析(三)--Request和Action的创建
- Picasso源码完全解析
- Picasso源码完全解析(五)--图片的获取(BitmapHunter)
- HandlerThread 的使用及其源码完全解析
- Android Handler、Message完全解析,带你从源码的角度彻底理解
- Android事件分发机制完全解析,带你从源码的角度彻底理解(下)
- Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
- Android Handler、Message完全解析,带你从源码的角度彻底理解
- Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
- Android AsyncTask完全解析,带你从源码的角度彻底理解
- Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
- Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
- Android AsyncTask完全解析,带你从源码的角度彻底理解