Picasso源碼解析

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

一竿屹、使用

Picasso.with(this)
        .load("url")
        .placeholder(R.drawable.leak_canary_icon)//占位圖
        .error(R.drawable.leak_canary_icon)//網(wǎng)絡(luò)失敗顯示的圖片
        .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: 圖片加載召嘶、轉(zhuǎn)換弄跌、緩存的管理類铛只。單列模式 淳玩,通過with方法獲取實例蜕着,也是加載圖片的入口蓖乘。
  2. RequestCreator: Request構(gòu)建類悄雅,Builder 模式众眨,采用鏈式設(shè)置該Request的屬性(如占位圖娩梨、緩存策略狈定、裁剪規(guī)則纽什、顯示大小、優(yōu)先級等等)让蕾。最后調(diào)用build()方法生成一個請求(Request)探孝。
  3. DeferredRequestCreator:當創(chuàng)建請求的時候還不能獲取ImageView的寬和高的時候顿颅,則創(chuàng)建一個DeferredRequestCreator,DeferredRequestCreator里對 target 設(shè)置監(jiān)聽栖疑,直到可以獲取到寬和高的時候重新執(zhí)行請求創(chuàng)建遇革。
  4. Action: 請求包裝類锻霎,存儲了該請求和RequestCreator設(shè)置的這些屬性旋恼,最終提交給線程執(zhí)行下載冰更。
  5. Dispatcher:分發(fā)器蜀细,分發(fā)執(zhí)行各種請求、分發(fā)結(jié)果等等归斤。
  6. PicassoExecutorService:Picasso使用的線程池刁岸,默認池大小為3。
  7. LruCache:一個使用最近最少使用策略的內(nèi)存緩存鸦难。
  8. BitmapHunter:這是Picasso的一個核心的類根吁,開啟線程執(zhí)行下載,獲取結(jié)果后解碼成Bitmap,然后做一些轉(zhuǎn)換操作如圖片旋轉(zhuǎn)合蔽、裁剪等击敌,如果請求設(shè)置了轉(zhuǎn)換器Transformation,也會在BitmapHunter里執(zhí)行這些轉(zhuǎn)換操作。
  9. NetworkRequestHandler:網(wǎng)絡(luò)請求處理器拴事,如果圖片需要從網(wǎng)絡(luò)下載沃斤,則用這個處理器處理。
  10. FileRequestHandler:文件請求處理器刃宵,如果請求的是一張存在文件中的圖片哮针,則用這個處理器處理缩抡。
  11. AssetRequestHandler: Asset 資源圖片處理器内边,如果是加載asset目錄下的圖片和屎,則用這個處理器處理随常。
  12. ResourceRequestHandler:Resource資源圖片處理器枣察,如果是加載res下的圖片猿涨,則用這個處理器處理红伦。
  13. ContentStreamRequestHandler: ContentProvider 處理器唠叛,如果是ContentProvider提供的圖片障般,則用這個處理器處理
  14. MediaStoreRequestHandler: MediaStore 請求處理器定拟,如果圖片是存在MediaStore上的則用這個處理器處理延窜。
    15.``Response`: 返回的結(jié)果信息商源,Stream流或者Bitmap缎除。
  15. Request: 請求實體類铸董,存儲了應(yīng)用在圖片上的信息颤芬。
  16. Target:圖片加載的監(jiān)聽器接口夺艰,有3個回調(diào)方法肥隆,onPrepareLoad 在請求提交前回調(diào)矾屯,onBitmapLoaded 請求成功回調(diào)亚情,并返回Bitmap,onBitmapFailed請求失敗回調(diào)栅迄。
  17. PicassoDrawable:繼承BitmapDrawable,實現(xiàn)了過渡動畫和圖片來源的標識(就是圖片來源的指示器泳叠,要調(diào)用 setIndicatorsEnabled(true)方法才生效),請求成功后都會包裝成BitmapDrawable顯示到ImageView 上锅棕。
  18. OkHttpDownloader:用OkHttp實現(xiàn)的圖片下載器移稳,默認就是用的這個下載器都许。
  19. UrlConnectionDownloader:使用HttpURLConnection 實現(xiàn)的下載器。
  20. MemoryPolicy: 內(nèi)存緩存策略暇昂,一個枚舉類型阱扬。
  21. NetworkPolicy: 磁盤緩存策略匈辱,一個枚舉類型焚虱。
  22. Stats: 這個類相當于日志記錄购裙,會記錄如:內(nèi)存緩存的命中次數(shù),丟失次數(shù)鹃栽,下載次數(shù)躏率,轉(zhuǎn)換次數(shù)等等,我們可以通過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. 配置內(nèi)存緩存饮亏,大小為手機內(nèi)存的15%
        cache = new LruCache(context);
      }
      if (service == null) {
       // 3.配置Picaso 線程池耍贾,核心池大小為3
        service = new PicassoExecutorService();
      }
      if (transformer == null) {
       // 4. 配置請求轉(zhuǎn)換器,默認的請求轉(zhuǎn)換器沒有做任何事路幸,直接返回原請求
        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. 配置內(nèi)存緩存:最多手機內(nèi)存的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. 配置默認請求轉(zhuǎn)換器
    // 空轉(zhuǎn)換荐开,請求原樣返回
    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)聽網(wǎng)絡(luò)變化
    this.receiver = new NetworkBroadcastReceiver(this);
    receiver.register();
  }

NetworkBroadcastReceiver監(jiān)聽網(wǎng)絡(luò)變化

  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)聽到網(wǎng)絡(luò)變化后简肴,dispatcher做響應(yīng)的操作
        dispatcher.dispatchNetworkStateChange(connectivityManager.getActiveNetworkInfo());
      }
    }
  }
  1. 監(jiān)聽到網(wǎng)絡(luò)變化后晃听,dispatcher做響應(yīng)的操作
  void performNetworkStateChange(NetworkInfo info) {
    if (service instanceof PicassoExecutorService) {
      // dispatcher 會根據(jù)網(wǎng)絡(luò)狀態(tài)調(diào)整線程池
      ((PicassoExecutorService) service).adjustThreadCount(info);
    }
    // Intentionally check only if isConnected() here before we flush out failed actions.
    if (info != null && info.isConnected()) {
      flushFailedActions();
    }
  }
 
// 根據(jù)網(wǎng)絡(luò)狀態(tài)調(diào)整線程池
 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 來構(gòu)建一個圖片下載請求

  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 來構(gòu)建請求能扒,如展位圖佣渴、大小、轉(zhuǎn)換器赫粥、裁剪等等,這些API其實是為對應(yīng)的屬性賦值,最終會傳入into方法中構(gòu)建請求予借。

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. 判斷是否設(shè)置了fit 屬性灵迫,如果設(shè)置了秦叛,再看是否能夠獲取ImageView 的寬高,如果獲取不到瀑粥,生成一個DeferredRequestCreator(延遲的請求管理器)挣跋,然后直接return,在DeferredRequestCreator中當監(jiān)聽到可以獲取ImageView 的寬高的時候,再執(zhí)行into方法狞换。

  2. 判斷是否從內(nèi)存緩存獲取圖片避咆,如果沒有設(shè)置NO_CACHE,則從內(nèi)存獲取,命中直接回調(diào)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;
    }
    //判斷是否設(shè)置了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);
    //是否從內(nèi)存緩存中獲取
    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());
    }
   //內(nèi)存緩存未命中或者設(shè)置了不從內(nèi)存緩存獲取剂府,則生成一個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表里查找有沒有對應(yīng)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));
      }
      //  調(diào)用hunt() 方法獲取最終結(jié)果
      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里面調(diào)用的是hunt方法來獲取結(jié)果,看一下hunt方法:

Bitmap hunt() throws IOException {
    Bitmap bitmap = null;
   // 是否從內(nèi)存緩存獲取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;
   // 請求處理器處理請求临扮,獲取結(jié)果论矾,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) {
            //如果需要做轉(zhuǎn)換杆勇,則在這里做轉(zhuǎn)換處理贪壳,如角度旋轉(zhuǎn),裁剪等蚜退。
            bitmap = transformResult(data, bitmap, exifRotation);
            if (picasso.loggingEnabled) {
              log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());
            }
          }
          if (data.hasCustomTransformations()) {
           // 如果配置了自定義轉(zhuǎn)換器闰靴,則在這里做轉(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;
  }

5钻注、Downloader 下載器下載圖片

hunt方法獲取結(jié)果的時候蚂且,最終調(diào)用的是配置的處理器的load方法

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

如果是網(wǎng)絡(luò)圖片會調(diào)用NetworkRequestHandlerload方法

@Override public Result load(Request request, int networkPolicy) throws IOException {
    //最終調(diào)用downloader的load方法獲取結(jié)果
    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最終是調(diào)用的downloader 的load方法下載圖片。內(nèi)置了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獲取結(jié)果后淑翼,分發(fā)器分發(fā)結(jié)果,通過Hander處理后,執(zhí)行performComplete方法:

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

Dispatcher:
  void performComplete(BitmapHunter hunter) {
    // 這里將結(jié)果緩存到內(nèi)存
    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;
        }
      //后面代碼省略
      ...
  };

回調(diào)到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;
   //將結(jié)果包裝成一個PicassoDrawable 并顯示
    PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);

    if (callback != null) {
      callback.onSuccess(); 回調(diào)callback
    }
  }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市惠豺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌风宁,老刑警劉巖洁墙,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異戒财,居然都是意外死亡热监,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門饮寞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來孝扛,“玉大人,你說我怎么就攤上這事幽崩】嗍迹” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵慌申,是天一觀的道長陌选。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么咨油? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任您炉,我火速辦了婚禮,結(jié)果婚禮上役电,老公的妹妹穿的比我還像新娘赚爵。我一直安慰自己,他們只是感情好法瑟,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布冀膝。 她就那樣靜靜地躺著,像睡著了一般瓢谢。 火紅的嫁衣襯著肌膚如雪畸写。 梳的紋絲不亂的頭發(fā)上驮瞧,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天氓扛,我揣著相機與錄音,去河邊找鬼论笔。 笑死采郎,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的狂魔。 我是一名探鬼主播蒜埋,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼最楷!你這毒婦竟也來了整份?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤籽孙,失蹤者是張志新(化名)和其女友劉穎烈评,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體犯建,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡讲冠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了适瓦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片竿开。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖玻熙,靈堂內(nèi)的尸體忽然破棺而出否彩,到底是詐尸還是另有隱情,我是刑警寧澤嗦随,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布胳搞,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏肌毅。R本人自食惡果不足惜筷转,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望悬而。 院中可真熱鬧呜舒,春花似錦、人聲如沸笨奠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽般婆。三九已至到腥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蔚袍,已是汗流浹背乡范。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留啤咽,地道東北人晋辆。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像宇整,于是被迫代替她去往敵國和親瓶佳。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

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

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