您的位置:首页 > 移动开发 > Android开发

Picasso源码完全解析(七)-- CleanupThread 取消请求

2017-05-17 11:37 846 查看


Picasso源码完全解析(一)--概述

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从队列里将它清除,并取消它对应的请求。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 源码 线程 图片