Picasso中的多线程

Picasso的使用及源码提交请求流程网上都已经很多了,本篇只分析Picasso中多线程相关

  1. 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(), new Utils.PicassoThreadFactory());
  }

可以看到核心线程数和最大线程数都是3个,并且使用了优先级队列,PriorityBlockingQueue() ,而此队列是无界队列,所以不需要最大线程数比核心线程数多且不需要设置保持存活时间。
那么问题来了,提交的时候优先级怎么排序的,看下面

//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
      implements Comparable {
    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();
    }
  }

仅仅只是设置了一下线程优先级为后台线程

  1. 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 extraRequestHandlers, Stats stats,
      Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled) {
...
this.referenceQueue = new ReferenceQueue();
    this.cleanupThread = new CleanupThread(referenceQueue, HANDLER);
    this.cleanupThread.start();
}
 
 

构建了一个ReferenceQueue,以及CleanupThread,看过LeakCanary的是不是立马明白原理了。
CleanupThread是一个守护者线程,如下

//Picasso.java
private static class CleanupThread extends Thread {
    CleanupThread(ReferenceQueue 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掉这一次的请求。

  1. 当然了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);
}

分析完毕

你可能感兴趣的:(Picasso中的多线程)