美文网首页
Picasso源码分析

Picasso源码分析

作者: wang_zd | 来源:发表于2017-01-20 12:03 被阅读32次

    源码基于Picasso 2.5.2。

    一,概括

    Picasso.with(context).load(“url”).into(imageView);
    

    从Picasso最简单的调用来一观Picasso的整体流程。
    给个图:

    Picasso调用流程.png

    with方法:通过Builder构建了一个Picasso单例。
    load方法:创建一个RequestCreator类,RequestCreator在构造方法创建了Request.Builder,可以通过Builder的build方法创建一个Request。
    into方法:通过Request构建了ImageViewAction,然后由Picasso添加到请求队列里。Picasso提交给Dispatcher ,Dispatcher 负责分发和处理Action,包括提交、暂停、继续、取消、网络状态变化、重试等等。加载完成后把结果在转发给Picasso来显示图片。整个调度流程头尾全由Picasso控制。

    //分析调用,代码有省略。
    Picasso类
    public static Picasso with(Context context) {
        if (singleton == null) {
          synchronized (Picasso.class) {
            if (singleton == null) {
              singleton = new Builder(context).build();
            }
          }
        }
        return singleton;
      }
    
    
    Picasso类
     public RequestCreator load(String path) {
         ***
        return load(Uri.parse(path));
      }
    Picasso类
     public RequestCreator load(Uri uri) {
        return new RequestCreator(this, uri, 0);
      }
    
    RequestCreator类
     RequestCreator(Picasso picasso, Uri uri, int resourceId) {
        this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
      }
    RequestCreator类
    public void into(ImageView target, Callback callback) {
        Request request = createRequest(started);
        String requestKey = createKey(request);
        Action action =
            new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
                errorDrawable, requestKey, tag, callback, noFade);
    
        picasso.enqueueAndSubmit(action);
    }
    Picasso类
    void enqueueAndSubmit(Action action) {
        submit(action);
      }
    void submit(Action action) {
        dispatcher.dispatchSubmit(action);
      }
    Dispatcher类
     void dispatchSubmit(Action action) {
        handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
      }
    handler的handleMessage处理发送的数据
    @Override public void handleMessage(final Message msg) {
          switch (msg.what) {
            case REQUEST_SUBMIT: {
              Action action = (Action) msg.obj;
              dispatcher.performSubmit(action);
              break;
            }
    }
    void performSubmit(Action action) {
        performSubmit(action, true);
      }
    void performSubmit(Action action, boolean dismissFailed) {
        hunter = forRequest(action.getPicasso(), this, cache, stats, action);
        //到此步,就把请求提交给线程池了。
        hunter.future = service.submit(hunter);
        hunterMap.put(action.getKey(), hunter);
    }
    
    Picasso的HANDLER处理返回的数据
       @Override public void handleMessage(Message msg) {
          switch (msg.what) {
            case HUNTER_BATCH_COMPLETE: {
              @SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
              //noinspection ForLoopReplaceableByForEach
              for (int i = 0, n = batch.size(); i < n; i++) {
                BitmapHunter hunter = batch.get(i);
                hunter.picasso.complete(hunter);
              }
              break;
            }
    
    void complete(BitmapHunter hunter) {
      if (single != null) {
          deliverAction(result, from, single);
        }
    }
    private void deliverAction(Bitmap result, LoadedFrom from, Action action) {
        action.complete(result, from);
    }
    ImageViewAction类
    @Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
      PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
    }
    最后一步显示到Imageview上
    static void setBitmap(ImageView target, Context context, Bitmap bitmap,
          Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
        Drawable placeholder = target.getDrawable();
        if (placeholder instanceof AnimationDrawable) {
          ((AnimationDrawable) placeholder).stop();
        }
        PicassoDrawable drawable =
            new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
        target.setImageDrawable(drawable);
      }
    

    二,细节分析

    Picasso使用单例+Builder模式创建对象。
    单例使用双重校验锁(DCL)实现,使用volatile的作用在于DCL实现单例还是存在一定的问题。
    单例模式详解:如何正确地写出单例模式

     static volatile Picasso singleton = null;
     public static Picasso with(Context context) {
        if (singleton == null) {
          synchronized (Picasso.class) {
            if (singleton == null) {
              singleton = new Builder(context).build();
            }
          }
        }
        return singleton;
      }
    

    Picasso会将传入的Context转为ApplicationContext,防止引起Activity和fragment内存泄露。

     public Builder(Context context) {
          if (context == null) {
            throw new IllegalArgumentException("Context must not be null.");
          }
          this.context = context.getApplicationContext();
        }
    
     public Picasso build() {
          Context context = this.context;
          //1.downloader
          if (downloader == null) {
            downloader = Utils.createDefaultDownloader(context);
          }
         //2.cache 
          if (cache == null) {
            cache = new LruCache(context);
          } 
         //3.service 
          if (service == null) {
            service = new PicassoExecutorService();
          }
         //4.transformer 
          if (transformer == null) {
            transformer = RequestTransformer.IDENTITY;
          }
         //5.stats
          Stats stats = new Stats(cache);
         //6.dispatcher 
          Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
    
          return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
              defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
        }
    

    2.1 Downloader

    Downloader内置两个子类,UrlConnectionDownloader和OkHttpDownloader。
    网络图片下载器,如果项目中有OkHttp就使用,没有就使用HttpURLConnection。源码版本2.5.2,不能反射到okhttp3,所以使用okhttp3作为Picasso的网络请求需要写一个支持okhttp3的Downloader。当然,肯定有人已经写过啦。JakeWharton大神的picasso2-okhttp3-downloader就是实现了这个功能。继续撸码go go。

    static Downloader createDefaultDownloader(Context context) {
        try {
         //反射
          Class.forName("com.squareup.okhttp.OkHttpClient");
          return OkHttpLoaderCreator.create(context);
        } catch (ClassNotFoundException ignored) {
        }
        return new UrlConnectionDownloader(context);
      }
    
    
    //OkHttpDownloader,最终会调用该构造方法
      public OkHttpDownloader(final File cacheDir, final long maxSize) {
       //new了一个OkHttpClient,连接超时默认15s,读写超时默认20s。
        this(defaultOkHttpClient());
        try {
          client.setCache(new com.squareup.okhttp.Cache(cacheDir, maxSize));
        } catch (IOException ignored) {
        }
      }
     //默认缓存文件,这就是硬盘缓存。
     //硬盘缓存使用的是网络框架的缓存,Picasso自己没有设计硬盘缓存。只有内存缓存(LruCache)
      static File createDefaultCacheDir(Context context) {
        File cache = new File(context.getApplicationContext().getCacheDir(), PICASSO_CACHE);
        if (!cache.exists()) {
          //noinspection ResultOfMethodCallIgnored
          cache.mkdirs();
        }
        return cache;
      }
    //计算硬盘能使用缓存空间的大小,返回数据size为该文件能使用的总空间的2%。
    //但是最大只能50M,最小5M。
    private static final int MIN_DISK_CACHE_SIZE = 5 * 1024 * 1024; // 5MB
    private static final int MAX_DISK_CACHE_SIZE = 50 * 1024 * 1024; // 50MB
     static long calculateDiskCacheSize(File dir) {
        long size = MIN_DISK_CACHE_SIZE;
    
        try {
         //get如何计算指定文件的大小。
          StatFs statFs = new StatFs(dir.getAbsolutePath());
          long available = ((long) statFs.getBlockCount()) * statFs.getBlockSize();
          // Target 2% of the total space.
          size = available / 50;
        } catch (IllegalArgumentException ignored) {
        }
    
        // Bound inside min/max size for disk cache.
        return Math.max(Math.min(size, MAX_DISK_CACHE_SIZE), MIN_DISK_CACHE_SIZE);
      }
    
    继续分析下UrlConnectionDownloader,在SDK14以后,
    也会设置硬盘缓存,用HttpResponseCache去实现的。缓存文件路径和大小和Okhttp相同。
     @Override public Response load(Uri uri, int networkPolicy) throws IOException {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
          installCacheIfNeeded(context);
        }
    }
     private static void installCacheIfNeeded(Context context) {
        // DCL + volatile should be safe after Java 5.
        if (cache == null) {
          try {
            synchronized (lock) {
              if (cache == null) {
                cache = ResponseCacheIcs.install(context);
              }
            }
          } catch (IOException ignored) {
          }
        }
      }
      private static class ResponseCacheIcs {
        static Object install(Context context) throws IOException {
          File cacheDir = Utils.createDefaultCacheDir(context);
          HttpResponseCache cache = HttpResponseCache.getInstalled();
          if (cache == null) {
            long maxSize = Utils.calculateDiskCacheSize(cacheDir);
            cache = HttpResponseCache.install(cacheDir, maxSize);
          }
          return cache;
        }
    
        static void close(Object cache) {
          try {
            ((HttpResponseCache) cache).close();
          } catch (IOException ignored) {
          }
        }
      }
    
    

    2.2 LruCache

    LRU算法主要依靠LinkedHashMap实现。用系统给应用分配内存的15%做缓存。在每次set之后都会trimToSize,当size满了就会开始移除数据。

    public LruCache(Context context) {
        this(Utils.calculateMemoryCacheSize(context));
      }
     static int calculateMemoryCacheSize(Context context) {
        ActivityManager am = getService(context, ACTIVITY_SERVICE);
        boolean largeHeap = (context.getApplicationInfo().flags & FLAG_LARGE_HEAP) != 0;
    //getMemoryClass()是系统为应用分配的内存。
        int memoryClass = am.getMemoryClass();
        if (largeHeap && SDK_INT >= HONEYCOMB) {
    //manifest文件中的<application>标签中largeHeap属性设置为true,获得应用可使用的最大内存,不建议使用
          memoryClass = ActivityManagerHoneycomb.getLargeMemoryClass(am);
        }
        // Target ~15% of the available heap.  获取~15%内存
        return 1024 * 1024 * memoryClass / 7;
      }
     public LruCache(int maxSize) {
        if (maxSize <= 0) {
          throw new IllegalArgumentException("Max size must be positive.");
        }
        this.maxSize = maxSize;
        this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
      }
     @Override public void set(String key, Bitmap bitmap) {
        if (key == null || bitmap == null) {
          throw new NullPointerException("key == null || bitmap == null");
        }
    
        Bitmap previous;
        synchronized (this) {
          putCount++;
          size += Utils.getBitmapBytes(bitmap);
          previous = map.put(key, bitmap);
          if (previous != null) {
            size -= Utils.getBitmapBytes(previous);
          }
        }
    
        trimToSize(maxSize);
      }
     private void trimToSize(int maxSize) {
        while (true) {
          String key;
          Bitmap value;
          synchronized (this) {
            if (size < 0 || (map.isEmpty() && size != 0)) {
              throw new IllegalStateException(
                  getClass().getName() + ".sizeOf() is reporting inconsistent results!");
            }
           //还有缓存空间直接返回。
            if (size <= maxSize || map.isEmpty()) {
              break;
            }
    
            Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();
            key = toEvict.getKey();
            value = toEvict.getValue();
            //取出最近最少使用的数据从map中移除。
            map.remove(key);
            //把使用的空间减少。
            size -= Utils.getBitmapBytes(value);
            evictionCount++;
          }
        }
      }
    

    2.3 PicassoExecutorService

    线程池,adjustThreadCount方法根据网络情况设置线程数数量,默认3个,wifi下4个,4G下3个,3G下2个,2G下1个。

    class PicassoExecutorService extends ThreadPoolExecutor {
     //默认线程数3个
      private static final int DEFAULT_THREAD_COUNT = 3;
    //核心线程和最大线程数相同,空闲线程等待时间0s。
      PicassoExecutorService() {
        super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,
            new PriorityBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory());
      }
     @Override
      public Future<?> submit(Runnable task) {
       //提交进来的task是BitmapHunter(Dispatcher里的performSubmit方法
       //hunter.future = service.submit(hunter);) 所以直接转。然后交给execute(),返回Future
        PicassoFutureTask ftask = new PicassoFutureTask((BitmapHunter) task);
        execute(ftask);
        return ftask;
      }
     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());
        }
      }
    }
    

    2.4 RequestTransformer

    请求转换。默认不改变,
    官方举例用途,如果使用CDN时,可以根据用户地址改变主机来加快图片访问速度

    if you use a CDN you can change the hostname for the image based on the current location of the user in order to get faster download speeds.

    调用时机,在RequestCreator的into方法创建Request时

    private Request createRequest(long started) {
        int id = nextId.getAndIncrement();
    
        Request request = data.build();
        request.id = id;
        request.started = started;
    
        boolean loggingEnabled = picasso.loggingEnabled;
        if (loggingEnabled) {
          log(OWNER_MAIN, VERB_CREATED, request.plainId(), request.toString());
        }
    
        Request transformed = picasso.transformRequest(request);
        if (transformed != request) {
          // If the request was changed, copy over the id and timestamp from the original.
          transformed.id = id;
          transformed.started = started;
    
          if (loggingEnabled) {
            log(OWNER_MAIN, VERB_CHANGED, transformed.logId(), "into " + transformed);
          }
        }
    
        return transformed;
      }
    

    2.5 Stats

    Stats的创建,用于统计缓存,缓存命中数,缓存丢失数。代码不长,全贴了。

    class Stats {
      private static final int CACHE_HIT = 0;
      private static final int CACHE_MISS = 1;
      private static final int BITMAP_DECODE_FINISHED = 2;
      private static final int BITMAP_TRANSFORMED_FINISHED = 3;
      private static final int DOWNLOAD_FINISHED = 4;
    
      private static final String STATS_THREAD_NAME = Utils.THREAD_PREFIX + "Stats";
    
      final HandlerThread statsThread;
      final Cache cache;
      final Handler handler;
      //记录的数据,缓存的命中数,丢失数,总下载大小,总原始bitmap大小等等。见名知意,安逸得很。
      long cacheHits;
      long cacheMisses;
      long totalDownloadSize;
      long totalOriginalBitmapSize;
      long totalTransformedBitmapSize;
      long averageDownloadSize;
      long averageOriginalBitmapSize;
      long averageTransformedBitmapSize;
      int downloadCount;
      int originalBitmapCount;
      int transformedBitmapCount;
    
      Stats(Cache cache) {
        this.cache = cache;
        //这里非常契合HandlerThread使用场景。单独线程去记录缓存的情况,处理各种不同情况的消息。
        this.statsThread = new HandlerThread(STATS_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);
        //不要忘记start
        this.statsThread.start();
        //细节方法。按注释的意思是在Android 5以前,HandlerThread会持有最后一个消息的引用   
        //为了处理这种情况,每秒都会发送一个空的新消息给handler,细节感动。
        Utils.flushStackLocalLeaks(statsThread.getLooper());
        this.handler = new StatsHandler(statsThread.getLooper(), this);
      }
    
      void dispatchBitmapDecoded(Bitmap bitmap) {
        processBitmap(bitmap, BITMAP_DECODE_FINISHED);
      }
    
      void dispatchBitmapTransformed(Bitmap bitmap) {
        processBitmap(bitmap, BITMAP_TRANSFORMED_FINISHED);
      }
    
      void dispatchDownloadFinished(long size) {
        handler.sendMessage(handler.obtainMessage(DOWNLOAD_FINISHED, size));
      }
    
      void dispatchCacheHit() {
        handler.sendEmptyMessage(CACHE_HIT);
      }
    
      void dispatchCacheMiss() {
        handler.sendEmptyMessage(CACHE_MISS);
      }
    
      void shutdown() {
        statsThread.quit();
      }
    
      void performCacheHit() {
        cacheHits++;
      }
    
      void performCacheMiss() {
        cacheMisses++;
      }
    
      void performDownloadFinished(Long size) {
        downloadCount++;
        totalDownloadSize += size;
        averageDownloadSize = getAverage(downloadCount, totalDownloadSize);
      }
    
      void performBitmapDecoded(long size) {
        originalBitmapCount++;
        totalOriginalBitmapSize += size;
        averageOriginalBitmapSize = getAverage(originalBitmapCount, totalOriginalBitmapSize);
      }
    
      void performBitmapTransformed(long size) {
        transformedBitmapCount++;
        totalTransformedBitmapSize += size;
        averageTransformedBitmapSize = getAverage(originalBitmapCount, totalTransformedBitmapSize);
      }
    
      StatsSnapshot createSnapshot() {
        return new StatsSnapshot(cache.maxSize(), cache.size(), cacheHits, cacheMisses,
            totalDownloadSize, totalOriginalBitmapSize, totalTransformedBitmapSize, averageDownloadSize,
            averageOriginalBitmapSize, averageTransformedBitmapSize, downloadCount, originalBitmapCount,
            transformedBitmapCount, System.currentTimeMillis());
      }
    
      private void processBitmap(Bitmap bitmap, int what) {
        // Never send bitmaps to the handler as they could be recycled before we process them.
        int bitmapSize = Utils.getBitmapBytes(bitmap);
        handler.sendMessage(handler.obtainMessage(what, bitmapSize, 0));
      }
    
      private static long getAverage(int count, long totalSize) {
        return totalSize / count;
      }
    
      private static class StatsHandler extends Handler {
    
        private final Stats stats;
    
        public StatsHandler(Looper looper, Stats stats) {
          super(looper);
          this.stats = stats;
        }
       //handleMessage对各种消息的处理。后面我们在看从哪里发出来的消息。
        @Override public void handleMessage(final Message msg) {
          switch (msg.what) {
            case CACHE_HIT:
              stats.performCacheHit();
              break;
            case CACHE_MISS:
              stats.performCacheMiss();
              break;
            case BITMAP_DECODE_FINISHED:
              stats.performBitmapDecoded(msg.arg1);
              break;
            case BITMAP_TRANSFORMED_FINISHED:
              stats.performBitmapTransformed(msg.arg1);
              break;
            case DOWNLOAD_FINISHED:
              stats.performDownloadFinished((Long) msg.obj);
              break;
            default:
              Picasso.HANDLER.post(new Runnable() {
                @Override public void run() {
                  throw new AssertionError("Unhandled stats message." + msg.what);
                }
              });
          }
        }
      }
    

    2.6 Dispatcher

    调度器,调度任务。

    class Dispatcher {
    //构造方法,传入前面生成的一些参数。这个mainThreadHandler是
    //Picasso类中的一个handler,图片下载成功后,Dispatcher会给这个handler发消息。
    Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
          Downloader downloader, Cache cache, Stats stats) {
        //HandlerThread子类
        this.dispatcherThread = new DispatcherThread();
        this.dispatcherThread.start();
        Utils.flushStackLocalLeaks(dispatcherThread.getLooper());
        this.context = context;
        this.service = service;
        this.hunterMap = new LinkedHashMap<String, BitmapHunter>();
        this.failedActions = new WeakHashMap<Object, Action>();
        this.pausedActions = new WeakHashMap<Object, Action>();
        this.pausedTags = new HashSet<Object>();
        this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
        this.downloader = downloader;
        this.mainThreadHandler = mainThreadHandler;
        this.cache = cache;
        this.stats = stats;
        this.batch = new ArrayList<BitmapHunter>(4);
        this.airplaneMode = Utils.isAirplaneModeOn(this.context);
        this.scansNetworkChanges = hasPermission(context, Manifest.permission.ACCESS_NETWORK_STATE);
        this.receiver = new NetworkBroadcastReceiver(this);
        receiver.register();
      }
    //这个类和Stats套路一样,用HandlerThread配合Handler调度处理各种请求。我们直接看handleMessage了。
    //消息来源主要在Picasso类中,比如分析的submit(),cancelExistingRequest(),pauseTag(),resumeTag()。
    } 
    
    private static class DispatcherHandler extends Handler {
        private final Dispatcher dispatcher;
    
        public DispatcherHandler(Looper looper, Dispatcher dispatcher) {
          super(looper);
          this.dispatcher = dispatcher;
        }
    
        @Override public void handleMessage(final Message msg) {
          switch (msg.what) {
            case REQUEST_SUBMIT: {
              Action action = (Action) msg.obj;
              dispatcher.performSubmit(action);
              break;
            }
            case REQUEST_CANCEL: {
              Action action = (Action) msg.obj;
              dispatcher.performCancel(action);
              break;
            }
            case TAG_PAUSE: {
              Object tag = msg.obj;
              dispatcher.performPauseTag(tag);
              break;
            }
            case TAG_RESUME: {
              Object tag = msg.obj;
              dispatcher.performResumeTag(tag);
              break;
            }
            case HUNTER_COMPLETE: {
              BitmapHunter hunter = (BitmapHunter) msg.obj;
              dispatcher.performComplete(hunter);
              break;
            }
            case HUNTER_RETRY: {
              BitmapHunter hunter = (BitmapHunter) msg.obj;
              dispatcher.performRetry(hunter);
              break;
            }
            case HUNTER_DECODE_FAILED: {
              BitmapHunter hunter = (BitmapHunter) msg.obj;
              dispatcher.performError(hunter, false);
              break;
            }
            case HUNTER_DELAY_NEXT_BATCH: {
              dispatcher.performBatchComplete();
              break;
            }
            case NETWORK_STATE_CHANGE: {
              NetworkInfo info = (NetworkInfo) msg.obj;
              dispatcher.performNetworkStateChange(info);
              break;
            }
            case AIRPLANE_MODE_CHANGE: {
              dispatcher.performAirplaneModeChange(msg.arg1 == AIRPLANE_MODE_ON);
              break;
            }
            default:
              Picasso.HANDLER.post(new Runnable() {
                @Override public void run() {
                  throw new AssertionError("Unknown handler message received: " + msg.what);
                }
              });
          }
        }
      }
    
    看看performSubmit方法
    void performSubmit(Action action, boolean dismissFailed) {
    //1.先看这条Action 是否被标记暂停了。标记了就直接放进pausedActions里。有日志的话就打印日志出来。
        if (pausedTags.contains(action.getTag())) {
          pausedActions.put(action.getTarget(), action);
          if (action.getPicasso().loggingEnabled) {
            log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),
                "because tag '" + action.getTag() + "' is paused");
          }
          return;
        }
    //2.再看hunterMap是否有这个Action,有了就让hunter attach到这个Action。
        BitmapHunter hunter = hunterMap.get(action.getKey());
        if (hunter != null) {
          hunter.attach(action);
          return;
        }
    //3.再看PicassoService是否被关了。
        if (service.isShutdown()) {
          if (action.getPicasso().loggingEnabled) {
            log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
          }
          return;
        }
    //这句话就必须撸下BitmapHunter了
        hunter = forRequest(action.getPicasso(), this, cache, stats, action);
    //把BitmapHunter提交的线程池里
        hunter.future = service.submit(hunter);
    //把hunter放进hunterMap里
        hunterMap.put(action.getKey(), hunter);
    //是否移除失败的Action
        if (dismissFailed) {
          failedActions.remove(action.getTarget());
        }
    //打印request的logID
        if (action.getPicasso().loggingEnabled) {
          log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
        }
      }
    

    跳出来看下RequestHandler,BitmapHunter

    //在Picasso构造方法里有这段代码,默认的给每个Action都添加的7个RequestHandler的实现类。
    //有处理Resource资源的ResourceRequestHandler
    //Asset资源的AssetRequestHandler
    //网络资源的NetworkRequestHandler
        int builtInHandlers = 7; // Adjust this as internal handlers are added or removed.
        int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
        List<RequestHandler> allRequestHandlers =
            new ArrayList<RequestHandler>(builtInHandlers + extraCount);
    
        // ResourceRequestHandler needs to be the first in the list to avoid
        // forcing other RequestHandlers to perform null checks on request.uri
        // to cover the (request.resourceId != 0) case.
        allRequestHandlers.add(new ResourceRequestHandler(context));
        if (extraRequestHandlers != null) {
          allRequestHandlers.addAll(extraRequestHandlers);
        }
        allRequestHandlers.add(new ContactsPhotoRequestHandler(context));
        allRequestHandlers.add(new MediaStoreRequestHandler(context));
        allRequestHandlers.add(new ContentStreamRequestHandler(context));
        allRequestHandlers.add(new AssetRequestHandler(context));
        allRequestHandlers.add(new FileRequestHandler(context));
        allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
    //设置了就不可以更改。
        requestHandlers = Collections.unmodifiableList(allRequestHandlers);
    

    这里以NetworkRequestHandler为例看一下

    class NetworkRequestHandler extends RequestHandler {
      static final int RETRY_COUNT = 2;
    
      private static final String SCHEME_HTTP = "http";
      private static final String SCHEME_HTTPS = "https";
    
      private final Downloader downloader;
      private final Stats stats;
    //构造方法,需要一个downloader和stats
      public NetworkRequestHandler(Downloader downloader, Stats stats) {
        this.downloader = downloader;
        this.stats = stats;
      }
    //是否可以处理这个Request,如果这个Request的uri的scheme是HTTP或者HTTPS就可以处理
      @Override public boolean canHandleRequest(Request data) {
        String scheme = data.uri.getScheme();
        return (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme));
      }
    //加载方法,response 由downloader.load()去下载
    //我们平常自己写个UrlConnection去加载图片的套路不就是在个
    //子线程里开启HttpURLConnection.openConnection(),然后
    //connection.getInputStream()。再把Stream转成Bitmap嘛。
    //downloader我们前面看了。就是这个原理。当然别个牛皮太多了。
    //这里就是网络请求,在把放到一个子线程里就行了,
    //BitmapHunter就是这个子线程的Runable
      @Override public Result load(Request request, int networkPolicy) throws IOException {
        Response response = downloader.load(request.uri, request.networkPolicy);
        if (response == null) {
          return null;
        }
    //是否由缓存中加载的
        Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;
    
        Bitmap bitmap = response.getBitmap();
        if (bitmap != null) {
          return new Result(bitmap, loadedFrom);
        }
    
        InputStream is = response.getInputStream();
        if (is == null) {
          return null;
        }
        // Sometimes response content length is zero when requests are being replayed. Haven't found
        // root cause to this but retrying the request seems safe to do so.
        if (loadedFrom == DISK && response.getContentLength() == 0) {
          Utils.closeQuietly(is);
          throw new ContentLengthException("Received response with 0 content-length header.");
        }
    //通知Stats计数了。
        if (loadedFrom == NETWORK && response.getContentLength() > 0) {
          stats.dispatchDownloadFinished(response.getContentLength());
        }
        return new Result(is, loadedFrom);
      }
    
      @Override int getRetryCount() {
        return RETRY_COUNT;
      }
    
      @Override boolean shouldRetry(boolean airplaneMode, NetworkInfo info) {
        return info == null || info.isConnected();
      }
    
      @Override boolean supportsReplay() {
        return true;
      }
    
      static class ContentLengthException extends IOException {
        public ContentLengthException(String message) {
          super(message);
        }
      }
    }
    
    
    class BitmapHunter implements Runnable {
    //构造方法
    BitmapHunter(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action,
          RequestHandler requestHandler) {
        this.sequence = SEQUENCE_GENERATOR.incrementAndGet();
        this.picasso = picasso;
        this.dispatcher = dispatcher;
        this.cache = cache;
        this.stats = stats;
        this.action = action;
        this.key = action.getKey();
        this.data = action.getRequest();
        this.priority = action.getPriority();
        this.memoryPolicy = action.getMemoryPolicy();
        this.networkPolicy = action.getNetworkPolicy();
        this.requestHandler = requestHandler;
        this.retryCount = requestHandler.getRetryCount();
      }
    //刚刚的forRequest方法
    static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
          Action action) {
        Request request = action.getRequest();
        List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
    
        // Index-based loop to avoid allocating an iterator.
        //noinspection ForLoopReplaceableByForEach
        for (int i = 0, count = requestHandlers.size(); i < count; i++) {
          RequestHandler requestHandler = requestHandlers.get(i);
         //找到一个可以处理这个Request类型的RequestHandler
          if (requestHandler.canHandleRequest(request)) {
            return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
          }
        }
    //找不到就抛异常
        return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);
      }
    
    //Runable最重要的就是run方法了
    @Override public void run() {
        try {
          updateThreadName(data);
    
          if (picasso.loggingEnabled) {
            log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));
          }
    //得到结果
          result = hunt();
    
          if (result == null) {
    //分发失败消息
            dispatcher.dispatchFailed(this);
          } else {
    //分发成功消息
            dispatcher.dispatchComplete(this);
          }
        } catch (Downloader.ResponseException e) {
          if (!e.localCacheOnly || e.responseCode != 504) {
            exception = e;
          }
          dispatcher.dispatchFailed(this);
        } catch (NetworkRequestHandler.ContentLengthException e) {
          exception = e;
          dispatcher.dispatchRetry(this);
        } catch (IOException e) {
          exception = e;
          dispatcher.dispatchRetry(this);
        } catch (OutOfMemoryError e) {
          StringWriter writer = new StringWriter();
          stats.createSnapshot().dump(new PrintWriter(writer));
          exception = new RuntimeException(writer.toString(), e);
          dispatcher.dispatchFailed(this);
        } catch (Exception e) {
          exception = e;
          dispatcher.dispatchFailed(this);
        } finally {
          Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);
        }
      }
     Bitmap hunt() throws IOException {
        Bitmap bitmap = null;
    //先去内存中找
        if (shouldReadFromMemoryCache(memoryPolicy)) {
          bitmap = cache.get(key);
          if (bitmap != null) {
            stats.dispatchCacheHit();
            loadedFrom = MEMORY;
            if (picasso.loggingEnabled) {
              log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");
            }
            return bitmap;
          }
        }
    
        data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
    //这里调用了RequestHandler的load方法,返回了Result
        RequestHandler.Result result = requestHandler.load(data, networkPolicy);
        if (result != null) {
          loadedFrom = result.getLoadedFrom();
          exifRotation = result.getExifOrientation();
    
          bitmap = result.getBitmap();
    
          // If there was no Bitmap then we need to decode it from the stream.
          if (bitmap == null) {
            InputStream is = result.getStream();
            try {
              bitmap = decodeStream(is, data);
            } finally {
              Utils.closeQuietly(is);
            }
          }
        }
    
        if (bitmap != null) {
          if (picasso.loggingEnabled) {
            log(OWNER_HUNTER, VERB_DECODED, data.logId());
          }
          stats.dispatchBitmapDecoded(bitmap);
          if (data.needsTransformation() || exifRotation != 0) {
            synchronized (DECODE_LOCK) {
    //图片是否需要转换处理。
              if (data.needsMatrixTransform() || exifRotation != 0) {
    //先是自带的转换,比如是否要转个角度啊,是否要centerCrop啊等。
                bitmap = transformResult(data, bitmap, exifRotation);
                if (picasso.loggingEnabled) {
                  log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());
                }
              }
              if (data.hasCustomTransformations()) {
    //再来自定义的转换。后面来些实例。
                bitmap = applyCustomTransformations(data.transformations, bitmap);
                if (picasso.loggingEnabled) {
                  log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations");
                }
              }
            }
            if (bitmap != null) {
              stats.dispatchBitmapTransformed(bitmap);
            }
          }
        }
    
        return bitmap;
      }
    }
    

    接下来看下dispatcher.performComplete(hunter)方法。在BitmapHunter中run方法result不为空就发送complete消息。就会调用此方法

     void performComplete(BitmapHunter hunter) {
    //判断是否写到内存
        if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
          cache.set(hunter.getKey(), hunter.getResult());
        }
    //把这个hunter可以移除了。
        hunterMap.remove(hunter.getKey());
        batch(hunter);
        if (hunter.getPicasso().loggingEnabled) {
          log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");
        }
      }
     private void batch(BitmapHunter hunter) {
        if (hunter.isCancelled()) {
          return;
        }
        batch.add(hunter);
    //如果没有这个消息就发
        if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
    //等200ms发送消息
    handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
        }
      }
    //调用这个方法,原来是把这段时间的hunter一起返回。用Picasso时是不是几张几张图片一起显示,
    //不是一张一张的加载,细节优化。完美
    //Dispatcher回调给Picasso,发送消息把结果带回去。
    void performBatchComplete() {
        List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
        batch.clear();
        mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
        logBatch(copy);
      }
    Picasso的handler处理COMPLETE。
    complete->deliverAction->action.complete()
    我们这是ImageViewAction,看看
      PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
    调用PicassoDrawable的setBitmap完成最后一步
      static void setBitmap(ImageView target, Context context, Bitmap bitmap,
          Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
        Drawable placeholder = target.getDrawable();
        if (placeholder instanceof AnimationDrawable) {
          ((AnimationDrawable) placeholder).stop();
        }
        PicassoDrawable drawable =
            new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
        target.setImageDrawable(drawable);
      }
    

    相关文章

      网友评论

          本文标题:Picasso源码分析

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