美文网首页
Picasso中的多线程

Picasso中的多线程

作者: GDHuo | 来源:发表于2019-07-25 12:39 被阅读0次

    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<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();
        }
      }
    

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

    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<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掉这一次的请求。

    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中的多线程

          本文链接:https://www.haomeiwen.com/subject/pyvcrctx.html