Picasso源碼分析

源碼基于Picasso 2.5.2。

一,概括

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

從Picasso最簡單的調(diào)用來一觀Picasso的整體流程。
給個圖:

Picasso調(diào)用流程.png

with方法:通過Builder構(gòu)建了一個Picasso單例。
load方法:創(chuàng)建一個RequestCreator類,RequestCreator在構(gòu)造方法創(chuàng)建了Request.Builder,可以通過Builder的build方法創(chuàng)建一個Request。
into方法:通過Request構(gòu)建了ImageViewAction,然后由Picasso添加到請求隊列里。Picasso提交給Dispatcher ,Dispatcher 負責分發(fā)和處理Action,包括提交、暫停、繼續(xù)、取消、網(wǎng)絡狀態(tài)變化、重試等等。加載完成后把結(jié)果在轉(zhuǎn)發(fā)給Picasso來顯示圖片。整個調(diào)度流程頭尾全由Picasso控制。

//分析調(diào)用,代碼有省略。
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處理發(fā)送的數(shù)據(jù)
@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處理返回的數(shù)據(jù)
   @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);
  }

二,細節(jié)分析

Picasso使用單例+Builder模式創(chuàng)建對象。
單例使用雙重校驗鎖(DCL)實現(xiàn),使用volatile的作用在于DCL實現(xiàn)單例還是存在一定的問題。
單例模式詳解:如何正確地寫出單例模式

 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轉(zhuǎn)為ApplicationContext,防止引起Activity和fragment內(nèi)存泄露。

 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內(nèi)置兩個子類,UrlConnectionDownloader和OkHttpDownloader。
網(wǎng)絡圖片下載器,如果項目中有OkHttp就使用,沒有就使用HttpURLConnection。源碼版本2.5.2,不能反射到okhttp3,所以使用okhttp3作為Picasso的網(wǎng)絡請求需要寫一個支持okhttp3的Downloader。當然,肯定有人已經(jīng)寫過啦。JakeWharton大神的picasso2-okhttp3-downloader就是實現(xiàn)了這個功能。繼續(xù)擼碼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,最終會調(diào)用該構(gòu)造方法
  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) {
    }
  }
 //默認緩存文件,這就是硬盤緩存。
 //硬盤緩存使用的是網(wǎng)絡框架的緩存,Picasso自己沒有設計硬盤緩存。只有內(nèi)存緩存(LruCache)
  static File createDefaultCacheDir(Context context) {
    File cache = new File(context.getApplicationContext().getCacheDir(), PICASSO_CACHE);
    if (!cache.exists()) {
      //noinspection ResultOfMethodCallIgnored
      cache.mkdirs();
    }
    return cache;
  }
//計算硬盤能使用緩存空間的大小,返回數(shù)據(jù)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);
  }

繼續(xù)分析下UrlConnectionDownloader,在SDK14以后,
也會設置硬盤緩存,用HttpResponseCache去實現(xiàn)的。緩存文件路徑和大小和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實現(xiàn)。用系統(tǒng)給應用分配內(nèi)存的15%做緩存。在每次set之后都會trimToSize,當size滿了就會開始移除數(shù)據(jù)。

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()是系統(tǒng)為應用分配的內(nèi)存。
    int memoryClass = am.getMemoryClass();
    if (largeHeap && SDK_INT >= HONEYCOMB) {
//manifest文件中的<application>標簽中l(wèi)argeHeap屬性設置為true,獲得應用可使用的最大內(nèi)存,不建議使用
      memoryClass = ActivityManagerHoneycomb.getLargeMemoryClass(am);
    }
    // Target ~15% of the available heap.  獲取~15%內(nèi)存
    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();
        //取出最近最少使用的數(shù)據(jù)從map中移除。
        map.remove(key);
        //把使用的空間減少。
        size -= Utils.getBitmapBytes(value);
        evictionCount++;
      }
    }
  }

2.3 PicassoExecutorService

線程池,adjustThreadCount方法根據(jù)網(wǎng)絡情況設置線程數(shù)數(shù)量,默認3個,wifi下4個,4G下3個,3G下2個,2G下1個。

class PicassoExecutorService extends ThreadPoolExecutor {
 //默認線程數(shù)3個
  private static final int DEFAULT_THREAD_COUNT = 3;
//核心線程和最大線程數(shù)相同,空閑線程等待時間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);) 所以直接轉(zhuǎn)。然后交給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.
      //高優(yōu)先級需要更小,排在前面
      // Equal priorities are sorted by sequence number to provide FIFO ordering.
      //相同優(yōu)先級就先進先出。
      return (p1 == p2 ? hunter.sequence - other.hunter.sequence : p2.ordinal() - p1.ordinal());
    }
  }
}

2.4 RequestTransformer

請求轉(zhuǎn)換。默認不改變,
官方舉例用途,如果使用CDN時,可以根據(jù)用戶地址改變主機來加快圖片訪問速度

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.

調(diào)用時機,在RequestCreator的into方法創(chuàng)建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的創(chuàng)建,用于統(tǒng)計緩存,緩存命中數(shù),緩存丟失數(shù)。代碼不長,全貼了。

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;
  //記錄的數(shù)據(jù),緩存的命中數(shù),丟失數(shù),總下載大小,總原始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();
    //細節(jié)方法。按注釋的意思是在Android 5以前,HandlerThread會持有最后一個消息的引用   
    //為了處理這種情況,每秒都會發(fā)送一個空的新消息給handler,細節(jié)感動。
    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對各種消息的處理。后面我們在看從哪里發(fā)出來的消息。
    @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

調(diào)度器,調(diào)度任務。

class Dispatcher {
//構(gòu)造方法,傳入前面生成的一些參數(shù)。這個mainThreadHandler是
//Picasso類中的一個handler,圖片下載成功后,Dispatcher會給這個handler發(fā)消息。
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調(diào)度處理各種請求。我們直接看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構(gòu)造方法里有這段代碼,默認的給每個Action都添加的7個RequestHandler的實現(xiàn)類。
//有處理Resource資源的ResourceRequestHandler
//Asset資源的AssetRequestHandler
//網(wǎng)絡資源的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;
//構(gòu)造方法,需要一個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轉(zhuǎn)成Bitmap嘛。
//downloader我們前面看了。就是這個原理。當然別個牛皮太多了。
//這里就是網(wǎng)絡請求,在把放到一個子線程里就行了,
//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計數(shù)了。
    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 {
//構(gòu)造方法
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));
      }
//得到結(jié)果
      result = hunt();

      if (result == null) {
//分發(fā)失敗消息
        dispatcher.dispatchFailed(this);
      } else {
//分發(fā)成功消息
        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;
//先去內(nèi)存中找
    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;
//這里調(diào)用了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) {
//圖片是否需要轉(zhuǎn)換處理。
          if (data.needsMatrixTransform() || exifRotation != 0) {
//先是自帶的轉(zhuǎn)換,比如是否要轉(zhuǎn)個角度啊,是否要centerCrop啊等。
            bitmap = transformResult(data, bitmap, exifRotation);
            if (picasso.loggingEnabled) {
              log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());
            }
          }
          if (data.hasCustomTransformations()) {
//再來自定義的轉(zhuǎn)換。后面來些實例。
            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不為空就發(fā)送complete消息。就會調(diào)用此方法

 void performComplete(BitmapHunter hunter) {
//判斷是否寫到內(nèi)存
    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);
//如果沒有這個消息就發(fā)
    if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
//等200ms發(fā)送消息
handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
    }
  }
//調(diào)用這個方法,原來是把這段時間的hunter一起返回。用Picasso時是不是幾張幾張圖片一起顯示,
//不是一張一張的加載,細節(jié)優(yōu)化。完美
//Dispatcher回調(diào)給Picasso,發(fā)送消息把結(jié)果帶回去。
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);
調(diào)用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);
  }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容

  • Picasso,看的版本是v.2.5.2 使用方法,大概這么幾種加載資源的形式 還可以對圖片進行一些操作:設置大小...
    Jinjins1129閱讀 362評論 0 3
  • 前一篇文章講了Picasso的詳細用法,Picasso 是一個強大的圖片加載緩存框架,一個非常優(yōu)秀的開源庫,學習一...
    依然范特稀西閱讀 4,638評論 13 24
  • 我們使用 Picasso 十分的簡單,只需調(diào)用下面一句就可以使用他了。 那么分析也從這里開始入手 Picasso對...
    銳_nmpoi閱讀 333評論 0 0
  • 概述 在Android開發(fā)界,大家都知到square出品必屬精品,圖片加載庫Picasso自然也是。本文就從源碼角...
    朔野閱讀 681評論 0 7
  • 龍從云,虎從風,英雄好漢各不同。堪奈何,人心隔,滿心為人,人皆笑我。呵呵呵! 要求少,競爭烈,軟刀殺人不覺味。幡然...
    寧為玉帥一點閱讀 186評論 0 0