Picasso源碼解析

Picasso 是一個強大的圖片加載緩存框架

一、使用

Picasso.with(this)
        .load("url")
        .placeholder(R.drawable.leak_canary_icon)//占位圖
        .error(R.drawable.leak_canary_icon)//網絡失敗顯示的圖片
        .resize(480, 480)//指定圖片的尺寸
        .centerCrop()//指定圖片縮放類型
        .rotate(90)
        .priority(Picasso.Priority.HIGH)//指定優(yōu)先級
        .tag("tag")
        .memoryPolicy(MemoryPolicy.NO_CACHE)
        .networkPolicy(NetworkPolicy.NO_CACHE)
        .into(imageView);

二、重要的類

  1. Picasso: 圖片加載、轉換、緩存的管理類。單列模式 ,通過with方法獲取實例,也是加載圖片的入口。
  2. RequestCreator: Request構建類,Builder 模式,采用鏈式設置該Request的屬性(如占位圖、緩存策略、裁剪規(guī)則、顯示大小、優(yōu)先級等等)。最后調用build()方法生成一個請求(Request)。
  3. DeferredRequestCreator:當創(chuàng)建請求的時候還不能獲取ImageView的寬和高的時候,則創(chuàng)建一個DeferredRequestCreator,DeferredRequestCreator里對 target 設置監(jiān)聽,直到可以獲取到寬和高的時候重新執(zhí)行請求創(chuàng)建。
  4. Action: 請求包裝類,存儲了該請求和RequestCreator設置的這些屬性,最終提交給線程執(zhí)行下載。
  5. Dispatcher:分發(fā)器,分發(fā)執(zhí)行各種請求、分發(fā)結果等等。
  6. PicassoExecutorService:Picasso使用的線程池,默認池大小為3。
  7. LruCache:一個使用最近最少使用策略的內存緩存。
  8. BitmapHunter:這是Picasso的一個核心的類,開啟線程執(zhí)行下載,獲取結果后解碼成Bitmap,然后做一些轉換操作如圖片旋轉、裁剪等,如果請求設置了轉換器Transformation,也會在BitmapHunter里執(zhí)行這些轉換操作。
  9. NetworkRequestHandler:網絡請求處理器,如果圖片需要從網絡下載,則用這個處理器處理。
  10. FileRequestHandler:文件請求處理器,如果請求的是一張存在文件中的圖片,則用這個處理器處理。
  11. AssetRequestHandler: Asset 資源圖片處理器,如果是加載asset目錄下的圖片,則用這個處理器處理。
  12. ResourceRequestHandler:Resource資源圖片處理器,如果是加載res下的圖片,則用這個處理器處理。
  13. ContentStreamRequestHandler: ContentProvider 處理器,如果是ContentProvider提供的圖片,則用這個處理器處理
  14. MediaStoreRequestHandler: MediaStore 請求處理器,如果圖片是存在MediaStore上的則用這個處理器處理。
    15.``Response`: 返回的結果信息,Stream流或者Bitmap。
  15. Request: 請求實體類,存儲了應用在圖片上的信息。
  16. Target:圖片加載的監(jiān)聽器接口,有3個回調方法,onPrepareLoad 在請求提交前回調,onBitmapLoaded 請求成功回調,并返回Bitmap,onBitmapFailed請求失敗回調。
  17. PicassoDrawable:繼承BitmapDrawable,實現了過渡動畫和圖片來源的標識(就是圖片來源的指示器,要調用 setIndicatorsEnabled(true)方法才生效),請求成功后都會包裝成BitmapDrawable顯示到ImageView 上。
  18. OkHttpDownloader:用OkHttp實現的圖片下載器,默認就是用的這個下載器。
  19. UrlConnectionDownloader:使用HttpURLConnection 實現的下載器。
  20. MemoryPolicy: 內存緩存策略,一個枚舉類型。
  21. NetworkPolicy: 磁盤緩存策略,一個枚舉類型。
  22. Stats: 這個類相當于日志記錄,會記錄如:內存緩存的命中次數,丟失次數,下載次數,轉換次數等等,我們可以通過StatsSnapshot類將日志打印出來,看一下整個項目的圖片加載情況。

三、源碼分析

1、獲取Picasso實例

Picasso采用單例模式

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

// 真正new 的地方在build()方法里
 public Picasso build() {
      Context context = this.context;

      if (downloader == null) {
        //1. 配置默認的下載器,首先通過反射獲取OkhttpClient,如果獲取到了,就使用OkHttpDwownloader作為默認下載器
        //如果獲取不到就使用UrlConnectionDownloader作為默認下載器
        downloader = Utils.createDefaultDownloader(context);
      }
      if (cache == null) {
       // 2. 配置內存緩存,大小為手機內存的15%
        cache = new LruCache(context);
      }
      if (service == null) {
       // 3.配置Picaso 線程池,核心池大小為3
        service = new PicassoExecutorService();
      }
      if (transformer == null) {
       // 4. 配置請求轉換器,默認的請求轉換器沒有做任何事,直接返回原請求
        transformer = RequestTransformer.IDENTITY;
      }

      Stats stats = new Stats(cache);
      //5.創(chuàng)建分發(fā)器 
      Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);

      return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
          defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
    }
  1. 配置默認的下載器
  static Downloader createDefaultDownloader(Context context) {
    try {
      // 查找項目中是否使用了OkHttp
      Class.forName("com.squareup.okhttp.OkHttpClient");
      return OkHttpLoaderCreator.create(context);
    } catch (ClassNotFoundException ignored) {
    }
    // 沒有使用OkHttp則用默認的HttpURLConnection
    return new UrlConnectionDownloader(context);
  }
  1. 配置內存緩存:最多手機內存的15%
  public LruCache(Context context) {
    this(Utils.calculateMemoryCacheSize(context));
  }

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

  static int calculateMemoryCacheSize(Context context) {
    ActivityManager am = getService(context, ACTIVITY_SERVICE);
    boolean largeHeap = (context.getApplicationInfo().flags & FLAG_LARGE_HEAP) != 0;
    int memoryClass = am.getMemoryClass();
    if (largeHeap && SDK_INT >= HONEYCOMB) {
      memoryClass = am.getLargeMemoryClass();
    }
    // Target ~15% of the available heap.
    return 1024 * 1024 * memoryClass / 7;
  }
  1. 配置線程池
  PicassoExecutorService() {
    super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,
        new PriorityBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory());
  }
  1. 配置默認請求轉換器
    // 空轉換,請求原樣返回
    RequestTransformer IDENTITY = new RequestTransformer() {
      @Override public Request transformRequest(Request request) {
        return request;
      }
    };
  1. Dispatcher分發(fā)器
  Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
      Downloader downloader, Cache cache, Stats stats) {
    // DispatcherThread繼承自HandlerThread,主要做一些耗時操作 
    this.dispatcherThread = new DispatcherThread();
    this.dispatcherThread.start();
    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>();
    // DispatcherThread子線程的handler
    this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
    this.downloader = downloader;
    // 主線程handler
    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);
    // 監(jiān)聽網絡變化
    this.receiver = new NetworkBroadcastReceiver(this);
    receiver.register();
  }

NetworkBroadcastReceiver監(jiān)聽網絡變化

  static class NetworkBroadcastReceiver extends BroadcastReceiver {
    static final String EXTRA_AIRPLANE_STATE = "state";

    private final Dispatcher dispatcher;

    NetworkBroadcastReceiver(Dispatcher dispatcher) {
      this.dispatcher = dispatcher;
    }

    @Override public void onReceive(Context context, Intent intent) {
      final String action = intent.getAction();
      if (ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
        if (!intent.hasExtra(EXTRA_AIRPLANE_STATE)) {
          return; 
        }
       dispatcher.dispatchAirplaneModeChange(intent.getBooleanExtra(EXTRA_AIRPLANE_STATE, false));
      } else if (CONNECTIVITY_ACTION.equals(action)) {
        ConnectivityManager connectivityManager = getService(context, CONNECTIVITY_SERVICE);
        // 監(jiān)聽到網絡變化后,dispatcher做響應的操作
        dispatcher.dispatchNetworkStateChange(connectivityManager.getActiveNetworkInfo());
      }
    }
  }
  1. 監(jiān)聽到網絡變化后,dispatcher做響應的操作
  void performNetworkStateChange(NetworkInfo info) {
    if (service instanceof PicassoExecutorService) {
      // dispatcher 會根據網絡狀態(tài)調整線程池
      ((PicassoExecutorService) service).adjustThreadCount(info);
    }
    // Intentionally check only if isConnected() here before we flush out failed actions.
    if (info != null && info.isConnected()) {
      flushFailedActions();
    }
  }
 
// 根據網絡狀態(tài)調整線程池
 void adjustThreadCount(NetworkInfo info) {
    if (info == null || !info.isConnectedOrConnecting()) {
      setThreadCount(DEFAULT_THREAD_COUNT);
      return;
    }
    switch (info.getType()) {
      case ConnectivityManager.TYPE_WIFI:
      case ConnectivityManager.TYPE_WIMAX:
      case ConnectivityManager.TYPE_ETHERNET:
        setThreadCount(4);
        break;
      case ConnectivityManager.TYPE_MOBILE:
        switch (info.getSubtype()) {
          case TelephonyManager.NETWORK_TYPE_LTE:  // 4G
          case TelephonyManager.NETWORK_TYPE_HSPAP:
          case TelephonyManager.NETWORK_TYPE_EHRPD:
            setThreadCount(3);
            break;
          case TelephonyManager.NETWORK_TYPE_UMTS: // 3G
          case TelephonyManager.NETWORK_TYPE_CDMA:
          case TelephonyManager.NETWORK_TYPE_EVDO_0:
          case TelephonyManager.NETWORK_TYPE_EVDO_A:
          case TelephonyManager.NETWORK_TYPE_EVDO_B:
            setThreadCount(2);
            break;
          case TelephonyManager.NETWORK_TYPE_GPRS: // 2G
          case TelephonyManager.NETWORK_TYPE_EDGE:
            setThreadCount(1);
            break;
          default:
            setThreadCount(DEFAULT_THREAD_COUNT);
        }
        break;
      default:
        setThreadCount(DEFAULT_THREAD_COUNT);
    }
  }

  private void setThreadCount(int threadCount) {
    setCorePoolSize(threadCount);
    setMaximumPoolSize(threadCount);
  }

2、load生成RequestCreator

通過load方法生成一個RequestCreator,用鏈式api 來構建一個圖片下載請求

  public RequestCreator load(String path) {
    if (path == null) {
      return new RequestCreator(this, null, 0);
    }
    if (path.trim().length() == 0) {
      throw new IllegalArgumentException("Path must not be empty.");
    }
    return load(Uri.parse(path));
  }

RequestCreator提供了很多的API 來構建請求,如展位圖、大小、轉換器、裁剪等等,這些API其實是為對應的屬性賦值,最終會傳入into方法中構建請求。

public class RequestCreator {
  private static final AtomicInteger nextId = new AtomicInteger();

  private final Picasso picasso;
  private final Request.Builder data;

  private boolean noFade;
  private boolean deferred;
  private boolean setPlaceholder = true;
  private int placeholderResId;
  private int errorResId;
  private int memoryPolicy;
  private int networkPolicy;
  private Drawable placeholderDrawable;
  private Drawable errorDrawable;
  private Object tag;
}

3、into添加View,并請求下載

into方法里面干了3件事情:

  1. 判斷是否設置了fit 屬性,如果設置了,再看是否能夠獲取ImageView 的寬高,如果獲取不到,生成一個DeferredRequestCreator(延遲的請求管理器),然后直接return,在DeferredRequestCreator中當監(jiān)聽到可以獲取ImageView 的寬高的時候,再執(zhí)行into方法。

  2. 判斷是否從內存緩存獲取圖片,如果沒有設置NO_CACHE,則從內存獲取,命中直接回調CallBack 并且顯示圖片。

  3. 如果緩存未命中,則生成一個Action,并提交Action。

 public void into(ImageView target, Callback callback) {
    long started = System.nanoTime();
   // 檢查是否在主線程
    checkMain();

    if (target == null) {
      throw new IllegalArgumentException("Target must not be null.");
    }
   //如果沒有url或者resourceId 則取消請求
    if (!data.hasImage()) {
      picasso.cancelRequest(target);
      if (setPlaceholder) {
        setPlaceholder(target, getPlaceholderDrawable());
      }
      return;
    }
    //判斷是否設置了fit屬性
    if (deferred) {
      if (data.hasSize()) {
        throw new IllegalStateException("Fit cannot be used with resize.");
      }
      int width = target.getWidth();
      int height = target.getHeight();
      if (width == 0 || height == 0) {
        if (setPlaceholder) {
          setPlaceholder(target, getPlaceholderDrawable());
        }
       //如果獲取不到寬高,生成一個DeferredRequestCreator(延遲的請求管理器),然后直接return,
      //在DeferredRequestCreator中當監(jiān)聽到可以獲取ImageView 的寬高的時候,再執(zhí)行into方法。
        picasso.defer(target, new DeferredRequestCreator(this, target, callback));
        return;
      }
      data.resize(width, height);
    }

    Request request = createRequest(started);
    String requestKey = createKey(request);
    //是否從內存緩存中獲取
    if (shouldReadFromMemoryCache(memoryPolicy)) {
      Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
      if (bitmap != null) {
       //緩存命中,取消請求,并顯示圖片
        picasso.cancelRequest(target);
        setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
        if (picasso.loggingEnabled) {
          log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
        }
        if (callback != null) {
          callback.onSuccess();
        }
        return;
      }
    }

    if (setPlaceholder) {
      setPlaceholder(target, getPlaceholderDrawable());
    }
   //內存緩存未命中或者設置了不從內存緩存獲取,則生成一個Action ,提交執(zhí)行。
    Action action =
        new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
            errorDrawable, requestKey, tag, callback, noFade);

    picasso.enqueueAndSubmit(action);// 提交請求
  }

request包裝類Action

abstract class Action<T> {
  static class RequestWeakReference<M> extends WeakReference<M> {
    final Action action;

    public RequestWeakReference(Action action, M referent, ReferenceQueue<? super M> q) {
      super(referent, q);
      this.action = action;
    }
  }

  final Picasso picasso;
  final Request request;
  final WeakReference<T> target;
  final boolean noFade;
  final int memoryPolicy;
  final int networkPolicy;
  final int errorResId;
  final Drawable errorDrawable;
  final String key;
  final Object tag;

  boolean willReplay;
  boolean cancelled;

  Action(Picasso picasso, T target, Request request, int memoryPolicy, int networkPolicy,
      int errorResId, Drawable errorDrawable, String key, Object tag, boolean noFade) {
    this.picasso = picasso;
    this.request = request;
    this.target =
        target == null ? null : new RequestWeakReference<T>(this, target, picasso.referenceQueue);
    this.memoryPolicy = memoryPolicy;
    this.networkPolicy = networkPolicy;
    this.noFade = noFade;
    this.errorResId = errorResId;
    this.errorDrawable = errorDrawable;
    this.key = key;
    this.tag = (tag != null ? tag : this);
  }
}

picasso.enqueueAndSubmit(action) 提交請求

  void enqueueAndSubmit(Action action) {
    Object target = action.getTarget();
    if (target != null && targetToAction.get(target) != action) {
      // This will also check we are on the main thread.
      cancelExistingRequest(target);
      //將action 保存到了一個Map 中,目標View作為key
      targetToAction.put(target, action);
    }
    submit(action);
  }
// 交給分發(fā)器分發(fā)提交請求
  void submit(Action action) {
    dispatcher.dispatchSubmit(action);
  }

Dispatcher:
 void performSubmit(Action action, boolean dismissFailed) {
    // 先查看保存暫停tag表里面沒有包含Action的tag,如果包含,則將Action 存到暫停Action表里
    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;
    }

    BitmapHunter hunter = hunterMap.get(action.getKey());
    if (hunter != null) {
      hunter.attach(action);
      return;
    }
   // 如果線程池北shutDown,直接return
    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);
    hunter.future = service.submit(hunter);//提交執(zhí)行
    hunterMap.put(action.getKey(), hunter);
    if (dismissFailed) {
      failedActions.remove(action.getTarget());
    }

    if (action.getPicasso().loggingEnabled) {
      log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
    }
  }

執(zhí)行請求提交

  1. 先查看保存暫停tag表里面沒有包含Action的tag,如果包含,則將Action 存到暫停Action表里
  2. 從BitmapHunter表里查找有沒有對應action的hunter,如果有直接attach
  3. 為這個請求生成一個BitmapHunter,提交給線程池執(zhí)行

4、BitmapHunter 下載圖片

BitmapHunter繼承Runnable,其實就是開啟一個線程執(zhí)行最終的下載

class BitmapHunter implements Runnable {
  @Override public void run() {
    try {
      updateThreadName(data);

      if (picasso.loggingEnabled) {
        log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));
      }
      //  調用hunt() 方法獲取最終結果
      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);
    }
  }
}

當將一個bitmapHunter submit 給一個線程池執(zhí)行的時候,就會執(zhí)行run() 方法,run里面調用的是hunt方法來獲取結果,看一下hunt方法:

Bitmap hunt() throws IOException {
    Bitmap bitmap = null;
   // 是否從內存緩存獲取Bitmap
    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;
   // 請求處理器處理請求,獲取結果,Result里可能是Bitmap,可能是Stream
    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) {
            //如果需要做轉換,則在這里做轉換處理,如角度旋轉,裁剪等。
            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;
  }

5、Downloader 下載器下載圖片

hunt方法獲取結果的時候,最終調用的是配置的處理器的load方法

RequestHandler.Result result = requestHandler.load(data, networkPolicy);

如果是網絡圖片會調用NetworkRequestHandlerload方法

@Override public Result load(Request request, int networkPolicy) throws IOException {
    //最終調用downloader的load方法獲取結果
    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;
    }
    return new Result(is, loadedFrom);
  }

etworkRequestHandler最終是調用的downloader 的load方法下載圖片。內置了2個Downloader,OkhttpDownloader和UrlConnectionDownloader 。我們以UrlConnectionDownloader為例,來看一下load方法:

  @Override public Response load(Uri uri, int networkPolicy) throws IOException {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
      installCacheIfNeeded(context);
    }

    HttpURLConnection connection = openConnection(uri);
    connection.setUseCaches(true);

    if (networkPolicy != 0) {
      String headerValue;

      if (NetworkPolicy.isOfflineOnly(networkPolicy)) {
        headerValue = FORCE_CACHE;
      } else {
        StringBuilder builder = CACHE_HEADER_BUILDER.get();
        builder.setLength(0);

        if (!NetworkPolicy.shouldReadFromDiskCache(networkPolicy)) {
          builder.append("no-cache");
        }
        if (!NetworkPolicy.shouldWriteToDiskCache(networkPolicy)) {
          if (builder.length() > 0) {
            builder.append(',');
          }
          builder.append("no-store");
        }

        headerValue = builder.toString();
      }

      connection.setRequestProperty("Cache-Control", headerValue);
    }

    int responseCode = connection.getResponseCode();
    if (responseCode >= 300) {
      connection.disconnect();
      throw new ResponseException(responseCode + " " + connection.getResponseMessage(),
          networkPolicy, responseCode);
    }

    long contentLength = connection.getHeaderFieldInt("Content-Length", -1);
    boolean fromCache = parseResponseSourceHeader(connection.getHeaderField(RESPONSE_SOURCE));
    // 最后獲取InputStream流包裝成Response返回
    return new Response(connection.getInputStream(), fromCache, contentLength);
  }

6、完成加載

在BitmapHunter獲取結果后,分發(fā)器分發(fā)結果,通過Hander處理后,執(zhí)行performComplete方法:

      result = hunt();
      if (result == null) {
        dispatcher.dispatchFailed(this);
      } else {
        dispatcher.dispatchComplete(this);
      }

Dispatcher:
  void performComplete(BitmapHunter hunter) {
    // 這里將結果緩存到內存
    if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
      cache.set(hunter.getKey(), hunter.getResult());
    }
    / 請求完畢,將hunter從表中移除
    hunterMap.remove(hunter.getKey());
    batch(hunter);
    }

然后將BitmapHunter添加到一個批處理列表,通過Hander發(fā)送一個批處理消息

  private void batch(BitmapHunter hunter) {
    if (hunter.isCancelled()) {
      return;
    }
    batch.add(hunter);
    if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
      handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
    }
  }

最后執(zhí)行performBatchComplete 方法,通過主線程的Handler送處理完成的消息

  void performBatchComplete() {
    List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
    batch.clear();
    mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
    logBatch(copy);
  }

最后在Picasso 中handleMessage,顯示圖片

 static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
    @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;
        }
      //后面代碼省略
      ...
  };

回調到ImageViewAction 的complete方法顯示圖片

@Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
    if (result == null) {
      throw new AssertionError(
          String.format("Attempted to complete action with no result!\n%s", this));
    }

    ImageView target = this.target.get();
    if (target == null) {
      return;
    }
    Context context = picasso.context;
    boolean indicatorsEnabled = picasso.indicatorsEnabled;
   //將結果包裝成一個PicassoDrawable 并顯示
    PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);

    if (callback != null) {
      callback.onSuccess(); 回調callback
    }
  }
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,333評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 98,491評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,263評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,946評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,708評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,409評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 48,939評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,774評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,641評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,872評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,650評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容

  • 一. 概述 Picasso是Square出品的一個非常精簡的圖片加載及緩存庫,其主要特點包括: 易寫易讀的流式編程...
    SparkInLee閱讀 1,099評論 2 11
  • 概述 Picasso是大名鼎鼎的Square公司提供的一個適用于Android的強大的圖片下載緩存庫。 簡單使用 ...
    憨人_Vivam閱讀 218評論 0 1
  • Picasso 是 Square 公司出品的一款十分優(yōu)秀的開源圖片框架,也是目前 Android 開發(fā)中十分流行的...
    N0tExpectErr0r閱讀 388評論 0 3
  • 參考文章: Picasso源碼解析 一、簡介 介紹:Picasso,可譯為“畢加索”,是Android中一個圖片加...
    千涯秋瑟閱讀 1,705評論 1 2
  • 匆匆那年,我們彼此交換了時間 也能陪在彼此身邊 會一起沖著鏡頭伴鬼臉 每天都是你追我感 從來都不會覺得厭 也會數星...
    CC歪Q閱讀 158評論 0 0