Picasso的使用及源码提交请求流程网上都已经很多了,本篇只分析Picasso中多线程相关
- Picasso的线程池
Picasso的线程池是在Picasso.Builder.build()方法中创建的
if (service == null) {
service = new PicassoExecutorService();
}
而PicassoExecutorService是继承自ThreadPoolExecutor
//初始化函数
private static final int DEFAULT_THREAD_COUNT = 3;
PicassoExecutorService() {
super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,
new PriorityBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory());
}
可以看到核心线程数和最大线程数都是3个,并且使用了优先级队列,PriorityBlockingQueue<Runnable>() ,而此队列是无界队列,所以不需要最大线程数比核心线程数多且不需要设置保持存活时间。
那么问题来了,提交的时候优先级怎么排序的,看下面
//PicassoExecutorService.class
@Override
public Future<?> submit(Runnable task) {
PicassoFutureTask ftask = new PicassoFutureTask((BitmapHunter) task);
execute(ftask);
return ftask;
}
重写了submit接口,返回了自封装的PicassoFutureTask,
private static final class PicassoFutureTask extends FutureTask<BitmapHunter>
implements Comparable<PicassoFutureTask> {
private final BitmapHunter hunter;
public PicassoFutureTask(BitmapHunter hunter) {
super(hunter, null);
this.hunter = hunter;
}
@Override
public int compareTo(PicassoFutureTask other) {
Picasso.Priority p1 = hunter.getPriority();
Picasso.Priority p2 = other.hunter.getPriority();
// High-priority requests are "lesser" so they are sorted to the front.
// Equal priorities are sorted by sequence number to provide FIFO ordering.
return (p1 == p2 ? hunter.sequence - other.hunter.sequence : p2.ordinal() - p1.ordinal());
}
}
其实主要是重写了compareTo接口,根据BitmapHunter的priority对请求排序,而BitmapHunter就是提交到线程池里的Runnable
继续看线程池用到的线程工厂new Utils.PicassoThreadFactory()
//Util.java
static class PicassoThreadFactory implements ThreadFactory {
@SuppressWarnings("NullableProblems")
public Thread newThread(Runnable r) {
return new PicassoThread(r);
}
}
private static class PicassoThread extends Thread {
public PicassoThread(Runnable r) {
super(r);
}
@Override public void run() {
Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
super.run();
}
}
仅仅只是设置了一下线程优先级为后台线程
- Picasso UI对应的handler为
static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
@Override public void handleMessage(Message msg) {
...
}
}
};
是个静态常量,生命周期是app的生命周期。为啥呢,原因是Picasso里的Context是applicationContext()。
//Picasso$Builder的构造函数
public Builder(Context context) {
if (context == null) {
throw new IllegalArgumentException("Context must not be null.");
}
this.context = context.getApplicationContext();
}
//build方法
public Picasso build() {
Context context = this.context;
...
return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}
那问题来了,下载图片的activity或者fragment销毁后不会内存泄漏吗?
请看下面
//Picasso构造函数
Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
RequestTransformer requestTransformer, List<RequestHandler> extraRequestHandlers, Stats stats,
Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) {
...
this.referenceQueue = new ReferenceQueue<Object>();
this.cleanupThread = new CleanupThread(referenceQueue, HANDLER);
this.cleanupThread.start();
}
构建了一个ReferenceQueue,以及CleanupThread,看过LeakCanary的是不是立马明白原理了。
CleanupThread是一个守护者线程,如下
//Picasso.java
private static class CleanupThread extends Thread {
CleanupThread(ReferenceQueue<Object> referenceQueue, Handler handler) {
...
setDaemon(true);
}
@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();
}
...
}
}
...
}
就是起来个守护线程,死循环阻塞查询referenceQueue里是不是有回收的对象,有的话发送REQUEST_GCED请求给UI线程的HANDLER。
//上面的UI线程HANDLER
public void handleMessage(Message msg) {
...
case REQUEST_GCED: {
Action action = (Action) msg.obj;
...
action.picasso.cancelExistingRequest(action.getTarget());
break;
}
}
//Picasso.java
private void cancelExistingRequest(Object target) {
checkMain();
Action action = targetToAction.remove(target);
if (action != null) {
...
dispatcher.dispatchCancel(action);
}
...
}
//Dispatcher.java
void dispatchCancel(Action action) {
handler.sendMessage(handler.obtainMessage(REQUEST_CANCEL, action));
}
//Dispatcher$DispatchHandler
@Override public void handleMessage(final Message msg) {
...
case REQUEST_CANCEL: {
Action action = (Action) msg.obj;
dispatcher.performCancel(action);
break;
}
}
//Dispatcher
void performCancel(Action action) {
String key = action.getKey();
BitmapHunter hunter = hunterMap.get(key);
if (hunter != null) {
hunter.detach(action);
if (hunter.cancel()) {
hunterMap.remove(key);
...
}
}
//BitmapHunter
boolean cancel() {
return action == null
&& (actions == null || actions.isEmpty())
&& future != null
&& future.cancel(false);
}
可以看到UI线程Handler收到cancel请求后调用dispatcher分派后台线程DispatcherHandler进而通过future给cancel掉这一次的请求。
- 当然了DispatcherHander是个后台线程,用于在后台分发请求,以及将任务提交到线程池、取消请求、将结果批量保存发送给UI线程等。主要就是将处理逻辑尽量放后台这样不阻塞UI。
是在Dispatcher初始化的时候构建的
Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
Downloader downloader, Cache cache, Stats stats) {
this.dispatcherThread = new DispatcherThread();
this.dispatcherThread.start();
...
this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
}
分析完毕
网友评论