Picasso2.71源碼解析之圖片加載框架的那些事兒

上一系列我們講述了Glide的簡單使用和其源碼解析,還沒看的同學可以移步去瀏覽:

Glide4.9圖片框架(一)之加載圖片的常規(guī)使用方式

講完了Glide及其源碼,我們繼續(xù)看看同樣作為圖片加載框架的Picasso熟丸,內(nèi)部源碼就是怎樣的呢,這里就不去分析它的使用了廊驼,因為和Glide如出一轍熙涤,我們主要還是看內(nèi)部源碼的加載流程,本次源碼版本為2.71828鞠绰。

Picasso的簡單使用

     Picasso.get()
            //相當于glide的with方法腰埂,初始化Picasso
            .load("url")
            //傳入圖片url,構(gòu)建requestCreater
            .placeholder(R.drawable.error)
            //設(shè)置占位圖
            .error(R.drawable.error)
            //設(shè)置加載錯誤占位圖
            .resize(480,800)
            //設(shè)置加載圖片的寬高
            .centerCrop()
             //設(shè)置scyletype
            .rotate(90)
            //設(shè)置旋轉(zhuǎn)90度
            .priority(Picasso.Priority.HIGH)
            //加載的優(yōu)先級蜈膨,不是決定性的因素
            .tag("list view")
            //給加載添加tag
            .memoryPolicy(MemoryPolicy.NO_CACHE)
            //設(shè)置沒有內(nèi)存緩存
            .into(imageview);

Picasso.get()

 public static Picasso get() {
    if (singleton == null) {
      synchronized (Picasso.class) {
        if (singleton == null) {
          if (PicassoProvider.context == null) {
            throw new IllegalStateException("context == null");
          }
          singleton = new Builder(PicassoProvider.context).build();
        }
      }
    }
    return singleton;
  }

get方法采用的雙重檢查鎖實現(xiàn)的單例屿笼,其中對PicassoProvider.context 判空,這里的context是什么呢翁巍,我們跟進去看一下:

public final class PicassoProvider extends ContentProvider {

  @SuppressLint("StaticFieldLeak") static Context context;

  @Override public boolean onCreate() {
    context = getContext();
    return true;
  }

  @Nullable @Override
  public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
      @Nullable String[] selectionArgs, @Nullable String sortOrder) {
    return null;
  }

  @Nullable @Override public String getType(@NonNull Uri uri) {
    return null;
  }

  @Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
    return null;
  }

  @Override public int delete(@NonNull Uri uri, @Nullable String selection,
      @Nullable String[] selectionArgs) {
    return 0;
  }

  @Override
  public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
      @Nullable String[] selectionArgs) {
    return 0;
  }
}

可以看到創(chuàng)建這個contentprovider就是為了獲取context驴一,從而不需要從get方法里每次再傳入context,那么回到上面的get方法灶壶,我們繼續(xù)看singleton = new Builder(PicassoProvider.context).build()中的Builder構(gòu)造方法:

   public Builder(@NonNull Context context) {
      if (context == null) {
        throw new IllegalArgumentException("Context must not be null.");
      }
      this.context = context.getApplicationContext();
    }

這里是直接獲取ApplicationContext肝断,因此圖片的加載是跟application綁定的。繼續(xù)看build()方法:

public Picasso build() {
      Context context = this.context;
      //創(chuàng)建下載器驰凛,這里直接封裝了OkHttp3的請求框架作為下載器
      if (downloader == null) {
        downloader = new OkHttp3Downloader(context);
      }
      // 創(chuàng)建LruCache緩存
      if (cache == null) {
        cache = new LruCache(context);
      }
      //創(chuàng)建線程池
      if (service == null) {
        service = new PicassoExecutorService();
      }
      //創(chuàng)建請求轉(zhuǎn)換器胸懈,默認沒有添加任何轉(zhuǎn)換則直接請求
      if (transformer == null) {
        transformer = RequestTransformer.IDENTITY;
      }
      Stats stats = new Stats(cache);
      Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
      return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
          defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
    }

build()之一 OkHttp3Downloader

public OkHttp3Downloader(final Context context) {
    this(Utils.createDefaultCacheDir(context));
  }

  static File createDefaultCacheDir(Context context) {
    File cache = new File(context.getApplicationContext().getCacheDir(), PICASSO_CACHE);
    if (!cache.exists()) {
      //noinspection ResultOfMethodCallIgnored
      cache.mkdirs();
    }
    return cache;
  }

public OkHttp3Downloader(final File cacheDir) {
    this(cacheDir, Utils.calculateDiskCacheSize(cacheDir));
  }

  static long calculateDiskCacheSize(File dir) {
    long size = MIN_DISK_CACHE_SIZE;
    try {
      StatFs statFs = new StatFs(dir.getAbsolutePath());
      //noinspection deprecation
      long blockCount =
          SDK_INT < JELLY_BEAN_MR2 ? (long) statFs.getBlockCount() : statFs.getBlockCountLong();
      //noinspection deprecation
      long blockSize =
          SDK_INT < JELLY_BEAN_MR2 ? (long) statFs.getBlockSize() : statFs.getBlockSizeLong();
      long available = blockCount * blockSize;
      // 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);
  }

  public OkHttp3Downloader(final File cacheDir, final long maxSize) {
    this(new OkHttpClient.Builder().cache(new Cache(cacheDir, maxSize)).build());
    sharedClient = false;
  }

先看OkHttp3Downloader的構(gòu)造方法,這里的OkHttp3Downloader是默認基于okhttp封裝的請求下載器恰响,我們看下createDefaultCacheDir方法趣钱,根據(jù)命名其實可以猜出這個方法就是創(chuàng)建默認的緩存文件的目錄,而這個目錄的路徑就是context.getApplicationContext().getCacheDir()渔隶,然后回到OkHttp3Downloader的this方法羔挡,繼續(xù)跟下去調(diào)用了calculateDiskCacheSize方法并把緩存目錄傳進去,跟進去看间唉,這個方法就是計算默認緩存文件的大小绞灼,通過獲取本地存儲空間來計算能緩存的最大文件空間長度是多少。然后繼續(xù)看最下面的OkHttp3Downloader的this方法呈野,通過新建OkHttpClient的Builder設(shè)置緩存的目錄和size并完成初始化低矮。

build()之二 LruCache內(nèi)存緩存

public final class LruCache implements Cache {
  final android.util.LruCache<String, LruCache.BitmapAndSize> cache;

  /** Create a cache using an appropriate portion of the available RAM as the maximum size. */
  public LruCache(@NonNull Context context) {
    this(Utils.calculateMemoryCacheSize(context));
  }
0
  /** Create a cache with a given maximum size in bytes. */
  public LruCache(int maxByteCount) {
    cache = new android.util.LruCache<String, LruCache.BitmapAndSize>(maxByteCount) {
      @Override protected int sizeOf(String key, BitmapAndSize value) {
        return value.byteCount;
      }
    };
  }

 static int calculateMemoryCacheSize(Context context) {
    ActivityManager am = getService(context, ACTIVITY_SERVICE);
    boolean largeHeap = (context.getApplicationInfo().flags & FLAG_LARGE_HEAP) != 0;
    int memoryClass = largeHeap ? am.getLargeMemoryClass() : am.getMemoryClass();
    // Target ~15% of the available heap.
    return (int) (1024L * 1024L * memoryClass / 7);
  }

這里我們還是只看簡單的構(gòu)造方法吧,其實內(nèi)部持有了一個LruCache引用被冒,相當于自己封裝了一層LruCache军掂,通過calculateMemoryCacheSize方法,計算能此初始化的內(nèi)存緩存的大小并完成LruCache初始化昨悼。這里是獲取app能用緩存的七分之一大小設(shè)置內(nèi)存緩存蝗锥,關(guān)于LRUCache后面我們會單獨拿出來分析源碼,只要理解作為最近最少的使用的緩存優(yōu)先會被回收就行了率触,我們接著看下面:

build()之三 PicassoExecutorService線程池

 if (service == null) {
        service = new PicassoExecutorService();
      }

class PicassoExecutorService extends ThreadPoolExecutor {
  private static final int DEFAULT_THREAD_COUNT = 3;

  PicassoExecutorService() {
    super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,
            new PriorityBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory());
  }
}

 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        throw new RuntimeException("Stub!");
    }

下面初始化了一個自定義的線程池PicassoExecutorService终议,繼承的還是自帶的線程池ThreadPoolExecutor,那么構(gòu)造方法里傳入的五個參數(shù)分別表示什么意思呢

  • corePoolSize
    表示默認初始化的核心線程數(shù)
  • maximumPoolSize
    表示線程池能創(chuàng)建的最大線程個數(shù)葱蝗,超過則不會繼續(xù)創(chuàng)建
  • keepAliveTime
    線程最大空閑時間
  • TimeUnit
    時間單位
  • BlockingQueue
    線程等待隊列
  • ThreadFactory
    線程創(chuàng)建工廠

build()之四 Dispatcher

class Dispatcher {

  Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
      Downloader downloader, Cache cache, Stats stats) {
    this.dispatcherThread = new DispatcherThread();
    this.context = context;
    this.service = service;
    this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
    this.downloader = downloader;
    this.mainThreadHandler = mainThreadHandler;
    this.cache = cache;
    this.stats = stats;
    this.receiver = new NetworkBroadcastReceiver(this);
    receiver.register();
  }

Dispatcher是一個比較重要的類穴张,這里只截出了幾個比較關(guān)鍵的參數(shù)信息,dispatcher里包含了緩存信息两曼,請求結(jié)果的調(diào)度皂甘,狀態(tài)的切換,其中最重要的兩個handler悼凑,DispatcherHandler和mainThreadHandler 偿枕,我們來看看內(nèi)部的消息的處理是怎么樣的:

private static class DispatcherHandler extends Handler {
    private final Dispatcher dispatcher;

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

可以看到DispatcherHandler里面全是對各種狀態(tài)和結(jié)果的回調(diào)和處理,DispatcherHandler的構(gòu)造函數(shù)傳入的是dispatcherThread.getLooper()户辫,那繼續(xù)看看這個dispatcherThread是什么意思:

  static class DispatcherThread extends HandlerThread {
    DispatcherThread() {
      super(Utils.THREAD_PREFIX + DISPATCHER_THREAD_NAME, THREAD_PRIORITY_BACKGROUND);
    }
  }

其實就是繼承的HandlerThread益老,這里不去細看,dispatcher的構(gòu)造方法中還有一個this.receiver = new NetworkBroadcastReceiver(this).那我們重點看下這個廣播接收者的onReceive方法:

 @Override public void onReceive(Context context, Intent intent) {
      if (intent == null) {
        return;
      }
      final String action = intent.getAction();
      if (ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
        if (!intent.hasExtra(EXTRA_AIRPLANE_STATE)) {
          return; // No airplane state, ignore it. Should we query Utils.isAirplaneModeOn?
        }
        dispatcher.dispatchAirplaneModeChange(intent.getBooleanExtra(EXTRA_AIRPLANE_STATE, false));
      } else if (CONNECTIVITY_ACTION.equals(action)) {
        ConnectivityManager connectivityManager = getService(context, CONNECTIVITY_SERVICE);
        dispatcher.dispatchNetworkStateChange(connectivityManager.getActiveNetworkInfo());
      }
    }

其實在接收廣播中可以看到對飛行模式進行了監(jiān)聽寸莫,ACTION_AIRPLANE_MODE_CHANGED捺萌,如果是飛行模式,就調(diào)用dispatcher.dispatchAirplaneModeChange方法膘茎,跟進去看一下:

 void dispatchAirplaneModeChange(boolean airplaneMode) {
    handler.sendMessage(handler.obtainMessage(AIRPLANE_MODE_CHANGE,
        airplaneMode ? AIRPLANE_MODE_ON : AIRPLANE_MODE_OFF, 0));
  }
 void performAirplaneModeChange(boolean airplaneMode) {
    this.airplaneMode = airplaneMode;
  }

這里最終就是記錄一個是否為飛行模式的布爾值桃纯,我們繼續(xù)看下面的判讀網(wǎng)絡(luò)狀態(tài)鏈接的部分,dispatcher.dispatchNetworkStateChange:

  void performNetworkStateChange(NetworkInfo info) {
    if (service instanceof PicassoExecutorService) {
      ((PicassoExecutorService) service).adjustThreadCount(info);
    }
    if (info != null && info.isConnected()) {
      flushFailedActions();
    }
  }

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

這里重點分析一下adjustThreadCount方法披坏,當網(wǎng)絡(luò)狀態(tài)改變的時候态坦,根據(jù)當前網(wǎng)絡(luò)的類型,創(chuàng)建不同的核心線程數(shù)棒拂,因為網(wǎng)絡(luò)的影響伞梯,可能線程池的核心線程數(shù)也受到影響玫氢,這個設(shè)計是相當有意思的
,也是Glide中沒有去做的東西谜诫,對網(wǎng)絡(luò)差的圖片加載來說漾峡,提高了加載的效率。繼續(xù)看flushFailedActions();方法:

  private void flushFailedActions() {
    if (!failedActions.isEmpty()) {
      Iterator<Action> iterator = failedActions.values().iterator();
      while (iterator.hasNext()) {
        Action action = iterator.next();
        iterator.remove();
        if (action.getPicasso().loggingEnabled) {
          log(OWNER_DISPATCHER, VERB_REPLAYING, action.getRequest().logId());
        }
        performSubmit(action, false);
      }
    }
  }

failedActions這里存儲的是第一次請求失敗需要重新請求的集合喻旷,那么這里是空的生逸,我們不繼續(xù)往下分析了,到這里整個Picasso的build方法就結(jié)束了且预,我們繼續(xù)往下分析load方法:

Picasso.load()

 public RequestCreator load(@Nullable Uri uri) {
    return new RequestCreator(this, uri, 0);
  }

  public RequestCreator load(@Nullable 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));
  }

  public RequestCreator load(@NonNull File file) {
    if (file == null) {
      return new RequestCreator(this, null, 0);
    }
    return load(Uri.fromFile(file));
  }

  public RequestCreator load(@DrawableRes int resourceId) {
    if (resourceId == 0) {
      throw new IllegalArgumentException("Resource ID must not be zero.");
    }
    return new RequestCreator(this, null, resourceId);
  }

Picasso的load方法槽袄,相對于Glide來說比較少,只有四個锋谐,分別是Uri遍尺、path、file涮拗、Drawable狮鸭。Uri、path多搀、file最中都是調(diào)用的load(@Nullable Uri uri) 方法歧蕉,Drawable也簡單,這里我們就看傳入字符串的情況康铭。最終調(diào)用的new RequestCreator(this, uri, 0)方法惯退,我們跟進去看RequestCreator主要是有什么作用:

RequestCreator

  RequestCreator(Picasso picasso, Uri uri, int resourceId) {
    if (picasso.shutdown) {
      throw new IllegalStateException(
          "Picasso instance already shut down. Cannot submit new requests.");
    }
    this.picasso = picasso;
    this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
  }
   Builder(Uri uri, int resourceId, Bitmap.Config bitmapConfig) {
      this.uri = uri;
      this.resourceId = resourceId;
      this.config = bitmapConfig;
    }

看代碼才發(fā)現(xiàn)RequestCreator其實是request的build的包裝類,由它生成request的build類从藤,然后build才能創(chuàng)建request催跪,還有一個作用就是RequestCreator這里包裝了一系列的鏈式調(diào)用夫嗓,包括placeholder将饺、error拥峦、fit车伞、centerCrop等等。到這里load方法就結(jié)束了佃蚜,最終我們直接看into方法:

RequestCreator.into(ImageView)

  public void into(ImageView target) {
    into(target, null);
  }
  public void into(ImageView target, Callback callback) {
    long started = System.nanoTime();
    checkMain();
    if (target == null) {
      throw new IllegalArgumentException("Target must not be null.");
    }
    if (!data.hasImage()) {
      picasso.cancelRequest(target);
      if (setPlaceholder) {
        setPlaceholder(target, getPlaceholderDrawable());
      }
      return;
    }
    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());
        }
        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 action =
        new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
            errorDrawable, requestKey, tag, callback, noFade);
    picasso.enqueueAndSubmit(action);
  }

    boolean hasImage() {
      return uri != null || resourceId != 0;
    }

     public RequestCreator fit() {
      deferred = true;
      return this;
     }

    boolean hasSize() {
      return targetWidth != 0 || targetHeight != 0;
    }

checkMain是檢查時否在主線程霎冯,不是則拋出異常恭取,我們直接看data.hasImage()方法妒貌,這里的data就是我們load時創(chuàng)建的request的build通危,而hasImage就是判讀當前加載的資源是不是空,如果是空則調(diào)用picasso.cancelRequest取消請求并且設(shè)置占位圖直接return灌曙。

接下來看deferred變量菊碟,這里只有我們設(shè)置了fit才會去執(zhí)行,那么假設(shè)我們設(shè)了fit在刺,data.hasSize實際上是判斷我們是否設(shè)置過resize方法逆害,如果設(shè)置了头镊,那么不能再調(diào)用fit方法,因為這兩者是沖突的魄幕。我們繼續(xù)看通過target也就是傳入的image view獲取寬和高相艇,如果獲取到的寬或者高有一個為0,表示imageview還沒能繪制成功梅垄,這里設(shè)置占位圖,并且調(diào)用picasso.defer方法输玷,我們跟進去看一下:

class DeferredRequestCreator implements OnPreDrawListener, OnAttachStateChangeListener {
  private final RequestCreator creator;
  @VisibleForTesting final WeakReference<ImageView> target;
  @VisibleForTesting Callback callback;
  DeferredRequestCreator(RequestCreator creator, ImageView target, Callback callback) {
    this.creator = creator;
    this.target = new WeakReference<>(target);
    this.callback = callback;
    target.addOnAttachStateChangeListener(this);
    if (target.getWindowToken() != null) {
      onViewAttachedToWindow(target);
    }
  }

  @Override public void onViewAttachedToWindow(View view) {
    view.getViewTreeObserver().addOnPreDrawListener(this);
  }

  @Override public void onViewDetachedFromWindow(View view) {
    view.getViewTreeObserver().removeOnPreDrawListener(this);
  }

  @Override public boolean onPreDraw() {
    ImageView target = this.target.get();
    if (target == null) {
      return true;
    }
    ViewTreeObserver vto = target.getViewTreeObserver();
    if (!vto.isAlive()) {
      return true;
    }
    int width = target.getWidth();
    int height = target.getHeight();
    if (width <= 0 || height <= 0) {
      return true;
    }
    target.removeOnAttachStateChangeListener(this);
    vto.removeOnPreDrawListener(this);
    this.target.clear();
    this.creator.unfit().resize(width, height).into(target, callback);
    return true;
  }

這里其實大致看出來DeferredRequestCreator 實現(xiàn)了OnPreDrawListener和OnAttachStateChangeListener 队丝,那么就是在image view繪制成功后,重新再去獲取寬和高欲鹏,并且調(diào)用creator.unfit().resize反法重新給build也就是data傳遞了新的圖片寬高机久。并將這個DeferredRequestCreator存儲到targetToDeferredRequestCreator集合中。那么回到上面的into方法赔嚎,如果沒有設(shè)置或fit膘盖,則開始調(diào)用Request request = createRequest(started)創(chuàng)建一個請求,我們跟進去看是如何創(chuàng)建的尤误。

createRequest()

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

public Request build() {
      ......
      if (priority == null) {
        priority = Priority.NORMAL;
      }
      return new Request(uri, resourceId, stableKey, transformations, targetWidth, targetHeight,
          centerCrop, centerInside, centerCropGravity, onlyScaleDown, rotationDegrees,
          rotationPivotX, rotationPivotY, hasRotationPivot, purgeable, config, priority);
    }

這里調(diào)用了data.build()創(chuàng)建Request侠畔,我們進去看一下,直看重點损晤,直接new了一個Request對象软棺,傳入了很多參數(shù),比如uri尤勋、資源id喘落、key、轉(zhuǎn)換最冰、寬高瘦棋、旋轉(zhuǎn)、scaletype暖哨、優(yōu)先級等赌朋。然后回到build方法,調(diào)用了transformRequest方法篇裁,其實如果沒有設(shè)置transform箕慧,這里等于沒有變化,所以回到createRequest方法茴恰,通過createKey(request)創(chuàng)建了一個請求key颠焦,然后調(diào)用了shouldReadFromMemoryCache方法,這里如果我們設(shè)置了可以存儲到內(nèi)存緩存往枣,那么是會先去從內(nèi)存緩存讀取的伐庭,所以看看下面的Picasso的quickMemoryCacheCheck方法:

獲取內(nèi)存緩存

  Bitmap quickMemoryCacheCheck(String key) {
    Bitmap cached = cache.get(key);
    if (cached != null) {
      stats.dispatchCacheHit();
    } else {
      stats.dispatchCacheMiss();
    }
    return cached;
  }

  void dispatchCacheHit() {
    handler.sendEmptyMessage(CACHE_HIT);
  }
@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);
            }
          });
      }
    }
 void performCacheHit() {
    cacheHits++;
  }
  void performCacheMiss() {
    cacheMisses++;
  }

這里很簡單粉渠,直接從Picasso的cache中獲取緩存的bitmap,如果沒有則調(diào)用stats.dispatchCacheHit()方法圾另,stats其實就是對各種結(jié)果和回調(diào)做一個通知的作用霸株。也可以看一下,最終調(diào)用到performCacheHit集乔,對成功獲取的緩存數(shù)++去件;那么dispatchCacheMiss應(yīng)該就是沒拿到緩存的計數(shù)++了。

回到上面的quickMemoryCacheCheck方法返回的bitmap,扰路,如果bitmap 不為空表示獲取緩存成功尤溜,沒必要繼續(xù)請求了,因此調(diào)用cancelRequest汗唱,然后調(diào)用setBitmap方法:

  static void setBitmap(ImageView target, Context context, Bitmap bitmap,
      Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
    Drawable placeholder = target.getDrawable();
    if (placeholder instanceof Animatable) {
      ((Animatable) placeholder).stop();
    }
    PicassoDrawable drawable =
        new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
    target.setImageDrawable(drawable);
  }

直接看重點的一行宫莱,target.setImageDrawable(drawable),到這里就是將bitmap設(shè)置到imageview了哩罪,PicassoDrawable 其實是對bitmap進行的封裝授霸。再回到獲取內(nèi)存緩存并setbitmap的位置,這里我們第一次加載應(yīng)該是沒有緩存的际插,所以我們繼續(xù)看下去碘耳,沒有緩存的情況。

setPlaceholder就是設(shè)置占位符框弛,這個不是重點藏畅,看下面的new ImageViewAction方法,傳入了picasso, target, request, memoryPolicy, networkPolicy, errorResId,errorDrawable, requestKey, tag, callback, noFade一系列參數(shù)功咒,生成了一個ImageViewAction愉阎,那么我們重點看這個action是干嘛的:

class ImageViewAction extends Action<ImageView> {

  Callback callback;

  ImageViewAction(Picasso picasso, ImageView imageView, Request data, int memoryPolicy,
      int networkPolicy, int errorResId, Drawable errorDrawable, String key, Object tag,
      Callback callback, boolean noFade) {
    super(picasso, imageView, data, memoryPolicy, networkPolicy, errorResId, errorDrawable, key,
        tag, noFade);
    this.callback = callback;
  }

  @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.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
    if (callback != null) {
      callback.onSuccess();
    }
  }

  @Override public void error(Exception e) {
    ImageView target = this.target.get();
    if (target == null) {
      return;
    }
    Drawable placeholder = target.getDrawable();
    if (placeholder instanceof Animatable) {
      ((Animatable) placeholder).stop();
    }
    if (errorResId != 0) {
      target.setImageResource(errorResId);
    } else if (errorDrawable != null) {
      target.setImageDrawable(errorDrawable);
    }
    if (callback != null) {
      callback.onError(e);
    }
  }

ImageViewAction 主要實現(xiàn)的方法就是complete和error方法,這兩個的調(diào)用還是父類action中的力奋,主要是對請求完成后的回調(diào)做處理榜旦,設(shè)置成功的圖片或者設(shè)置加載出錯的圖片。所以主要的功能還是再父類Action中景殷〗δ兀回到ImageViewAction創(chuàng)建完成后,into方法的最后一行猿挚,調(diào)用的是picasso.enqueueAndSubmit(action)執(zhí)行這個action咐旧,我們跟進去看一下:

執(zhí)行請求

  void enqueueAndSubmit(Action action) {
    Object target = action.getTarget();
    if (target != null && targetToAction.get(target) != action) {
      cancelExistingRequest(target);
      targetToAction.put(target, action);
    }
    submit(action);
  }

  void submit(Action action) {
    dispatcher.dispatchSubmit(action);
  }

  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) {
    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;
    }
    if (service.isShutdown()) {
      if (action.getPicasso().loggingEnabled) {
        log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
      }
      return;
    }
    hunter = forRequest(action.getPicasso(), this, cache, stats, action);
    hunter.future = service.submit(hunter);
    hunterMap.put(action.getKey(), hunter);
    if (dismissFailed) {
      failedActions.remove(action.getTarget());
    }
    if (action.getPicasso().loggingEnabled) {
      log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
    }
  }

這里的if判斷,就是判斷如果之前存儲過跟這次不一樣的加載請求绩蜻,那么取消掉上一次的加載铣墨,存儲當前這一次的加載請求,然后調(diào)用submit方法办绝,我們看submit方法通過dispatcher下發(fā)了提交請求任務(wù)的消息伊约,最終調(diào)用到了performSubmit方法姚淆。這里我們看第一個if判斷

在了解判斷之前,來看看這個pausedTags是什么屡律,怎么賦值的:

public class SampleScrollListener implements AbsListView.OnScrollListener {
  private final Context context;

  public SampleScrollListener(Context context) {
    this.context = context;
  }

  @Override
  public void onScrollStateChanged(AbsListView view, int scrollState) {
    final Picasso picasso = Picasso.get();
    if (scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_TOUCH_SCROLL) {
      picasso.resumeTag(context);
    } else {
      picasso.pauseTag(context);
    }
  }
}

最終發(fā)現(xiàn)腌逢,pausedTags添加的地方,就是滾動監(jiān)聽超埋,當屏幕暫停時搏讶,就將當前的context存儲到暫停的tag中,否則就從暫停的pausedTags中移除霍殴。那么回到上面的performSubmit方法我們就明白了媒惕,意思就是只有當屏幕沒有滑動的時候,才允許去加載當前的請求繁成,那么繼續(xù)往下看吓笙,hunterMap.get方法獲取到了BitmapHunter淑玫,這里的action.getKey()就是前面我們獲取內(nèi)存緩存的key巾腕,那么bitmapHunter是什么呢,他說一個實現(xiàn)了Runnable接口的類絮蒿,所有的耗時操作包括請求圖片尊搬,解碼圖片,圖片轉(zhuǎn)換等都在bitmapHunter中執(zhí)行土涝。這里獲取BitmapHunter 后去判斷是否為空佛寿,如果不為空表示當前正在執(zhí)行請求,那么就不會再去執(zhí)行直接return但壮,否則我們看下面的forRequest方法:

  static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats,
      Action action) {
    Request request = action.getRequest();
    List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
    for (int i = 0, count = requestHandlers.size(); i < count; i++) {
      RequestHandler requestHandler = requestHandlers.get(i);
      if (requestHandler.canHandleRequest(request)) {
        return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
      }
    }
    return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);
  }

這里沒有BitmapHunter 冀泻,那么就去創(chuàng)建一個BitmapHunter 并且返回,通過找到對應(yīng)的requestHandlers 去生成對應(yīng)的BitmapHunter蜡饵,這里我們地方requestHandler應(yīng)該就是請求相關(guān)的NetworkRequestHandler弹渔,我們回到返回值的地方,下面調(diào)用service.submit(hunter)表示去執(zhí)行請求溯祸,請求的結(jié)果存儲在hunter.future中肢专,完成后緩存到hunterMap中,我們跟進去看一下:

  @Override
  public Future<?> submit(Runnable task) {
    PicassoFutureTask ftask = new PicassoFutureTask((BitmapHunter) task);
    execute(ftask);
    return ftask;
  }

@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 (NetworkRequestHandler.ResponseException e) {
      if (!NetworkPolicy.isOfflineOnly(e.networkPolicy) || e.code != 504) {
        exception = e;
      }
      dispatcher.dispatchFailed(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);
    }
  }

這里直接執(zhí)行execute方法焦辅,那么我們就看BitmapHunter的run方法了博杖,直接看result = hunt();這一行,可以看到通過hunt方法拿到請求的返回值了筷登,result是一個Bitmap類型剃根,那么我們就重點看hunt方法了。

hunt()

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

    networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
    RequestHandler.Result result = requestHandler.load(data, networkPolicy);
    if (result != null) {
      loadedFrom = result.getLoadedFrom();
      exifOrientation = result.getExifOrientation();
      bitmap = result.getBitmap();

      // If there was no Bitmap then we need to decode it from the stream.
      if (bitmap == null) {
        Source source = result.getSource();
        try {
          bitmap = decodeStream(source, data);
        } finally {
          try {
            //noinspection ConstantConditions If bitmap is null then source is guranteed non-null.
            source.close();
          } catch (IOException ignored) {
          }
        }
      }
    }

    if (bitmap != null) {
      if (picasso.loggingEnabled) {
        log(OWNER_HUNTER, VERB_DECODED, data.logId());
      }
      stats.dispatchBitmapDecoded(bitmap);
      if (data.needsTransformation() || exifOrientation != 0) {
        synchronized (DECODE_LOCK) {
          if (data.needsMatrixTransform() || exifOrientation != 0) {
            bitmap = transformResult(data, bitmap, exifOrientation);
            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;
  }

這里我們又調(diào)了一次shouldReadFromMemoryCache前方,這里再次獲取內(nèi)存緩存也沒有沖突跟继,防止前面請求的時候獲取緩存不及時种冬,這里我們直接看requestHandler.load返回RequestHandler.Result的方法,跟進去看一下:

  @Override public Result load(Request request, int networkPolicy) throws IOException {
    okhttp3.Request downloaderRequest = createRequest(request, networkPolicy);
    Response response = downloader.load(downloaderRequest);
    ResponseBody body = response.body();
    if (!response.isSuccessful()) {
      body.close();
      throw new ResponseException(response.code(), request.networkPolicy);
    }
    Picasso.LoadedFrom loadedFrom = response.cacheResponse() == null ? NETWORK : DISK;
    if (loadedFrom == DISK && body.contentLength() == 0) {
      body.close();
      throw new ContentLengthException("Received response with 0 content-length header.");
    }
    if (loadedFrom == NETWORK && body.contentLength() > 0) {
      stats.dispatchDownloadFinished(body.contentLength());
    }
    return new Result(body.source(), loadedFrom);
  }

  @NonNull @Override public Response load(@NonNull Request request) throws IOException {
    return client.newCall(request).execute();
  }

到這里就是直接通過okhttp3請求去獲取結(jié)果了舔糖,可能你奇怪為什么看到這里一直都是內(nèi)存緩存娱两,沒有看到過磁盤緩存,因為磁盤緩存是在okhttp3請求框架里面自己去做了金吗,所以這個代碼里面是沒有的十兢。看downloader.load方法內(nèi)部其實還是使用的OK HTTP的newCall創(chuàng)建請求并執(zhí)行的摇庙,那么回調(diào)結(jié)果旱物,返回上一層通過result.getBitmap()獲取到bitmap對象,如果為空卫袒,那么就再去解碼拿到Source并轉(zhuǎn)換成bitmap宵呛。后面就是回調(diào)了,返回到BitmapHunter的Run方法夕凝,拿到bitmap后調(diào)用dispatcher.dispatchComplete(this)方法:

拿到bitmap宝穗,回調(diào)結(jié)果

  void performComplete(BitmapHunter hunter) {
    if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
      cache.set(hunter.getKey(), hunter.getResult());
    }
    hunterMap.remove(hunter.getKey());
    batch(hunter);
    if (hunter.getPicasso().loggingEnabled) {
      log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");
    }
  }

在拿到hunter后,立馬通過判斷是否需要內(nèi)存緩存來決定是否將結(jié)果緩存到內(nèi)存緩存码秉,而下面的hunterMap也在請求成功后移除對應(yīng)的請求緩存逮矛,然后調(diào)用batch方法:

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

拿到hunter.result后調(diào)用bitmap的prepareToDraw方法準備填充到image view,然后存儲到batch中转砖。方便后面對bitmapHunter同意管理:

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) {
    Action single = hunter.getAction();
    List<Action> joined = hunter.getActions();
    boolean hasMultiple = joined != null && !joined.isEmpty();
    boolean shouldDeliver = single != null || hasMultiple;
    if (!shouldDeliver) {
      return;
    }
    Uri uri = hunter.getData().uri;
    Exception exception = hunter.getException();
    Bitmap result = hunter.getResult();
    LoadedFrom from = hunter.getLoadedFrom();
    if (single != null) {
      deliverAction(result, from, single, exception);
    }
    if (hasMultiple) {
      //noinspection ForLoopReplaceableByForEach
      for (int i = 0, n = joined.size(); i < n; i++) {
        Action join = joined.get(i);
        deliverAction(result, from, join, exception);
      }
    }
    if (listener != null && exception != null) {
      listener.onImageLoadFailed(this, uri, exception);
    }
  }

第一行的single也就是表示是否只有這一個须鼎,如果是則獲取到bitmap后直接回調(diào),否則就遍歷每一個Action都調(diào)用deliverAction回調(diào)府蔗,我們繼續(xù)看deliverAction方法:

private void deliverAction(Bitmap result, LoadedFrom from, Action action, Exception e) {
    if (action.isCancelled()) {
      return;
    }
    if (!action.willReplay()) {
      targetToAction.remove(action.getTarget());
    }
    if (result != null) {
      if (from == null) {
        throw new AssertionError("LoadedFrom cannot be null.");
      }
      action.complete(result, from);
      if (loggingEnabled) {
        log(OWNER_MAIN, VERB_COMPLETED, action.request.logId(), "from " + from);
      }
    } else {
      action.error(e);
      if (loggingEnabled) {
        log(OWNER_MAIN, VERB_ERRORED, action.request.logId(), e.getMessage());
      }
    }
  }

重點還是關(guān)注在action.complete(result, from)方法:

  @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.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
    if (callback != null) {
      callback.onSuccess();
    }
  }

 static void setBitmap(ImageView target, Context context, Bitmap bitmap,
      Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
    Drawable placeholder = target.getDrawable();
    if (placeholder instanceof Animatable) {
      ((Animatable) placeholder).stop();
    }
    PicassoDrawable drawable =
        new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
    target.setImageDrawable(drawable);
  }

最終調(diào)用action.complete實際上還是調(diào)用的image viewAction晋控,因為一開始的into方法,我們創(chuàng)建的action就是image viewAction姓赤,因此在complete中可以看到target.get()獲取我們傳進來的imageview赡译,然后調(diào)用PicassoDrawable的setBitmap方法設(shè)置圖片到view,最終調(diào)用callback.onSuccess()回調(diào)到我們使用Picasso加載圖片到地方模捂,前提是我們設(shè)置了這個callback捶朵。

總結(jié)

Picasso通過get方法創(chuàng)建單例實例,然后通過build方法創(chuàng)建請求下載器狂男、內(nèi)存緩存综看、線程池和Dispatcher ,并最終生成Picasso對象岖食。磁盤緩存和內(nèi)存緩存都是根據(jù)手機容量和內(nèi)存來判斷的红碑,內(nèi)存為app可用內(nèi)存的七分之一。Dispatcher則在全程中起到的一個調(diào)度的作用,回調(diào)和傳遞各種信息析珊,內(nèi)部有一個網(wǎng)絡(luò)狀態(tài)的廣播監(jiān)聽羡鸥,根據(jù)手機網(wǎng)絡(luò)的情況2G-4G來設(shè)置線程池不同的核心線程數(shù),然后還可以監(jiān)聽飛行模式忠寻。load方法則是通過RequestCreator 創(chuàng)建Request的build對象惧浴,最終調(diào)用into方法通過build對象創(chuàng)建Request對象并生成一個緩存key,通過緩存key在內(nèi)存緩存中查到是否存在緩存奕剃,不存在則創(chuàng)建一個Action對象衷旅,通過action對象找到對應(yīng)NetworkRequestHandler 并生成BitmapHunter,BitmapHunter本身是一個線程纵朋,最終調(diào)用PicassoExecutorService線程池的submit方法執(zhí)行BitmapHunter柿顶,最終調(diào)用requestHandler的load方法通過OK HTTP請求做請求和磁盤緩存的操作獲得Response ,然后解碼拿到bitmap回調(diào)給dispatcher操软,然后緩存到內(nèi)存嘁锯,并且回調(diào)到image viewAction的complete方法,最終通過PicassoDrawable的setBitmap方法設(shè)置圖片資源到image view中聂薪。

到這里Picasso的源碼分析就結(jié)束了家乘,在看過了Glide源碼之后,再看Picasso可能突然覺得輕松了許多胆建,畢竟Glide的功能還是很豐富的烤低,類有比較復雜肘交,所以閱讀起來相對比較麻煩笆载。那么對比我們之前分析的Glide源碼,我們看看他們的區(qū)別在哪:

  • 加載數(shù)據(jù)類型的區(qū)別
    glide支持gif圖加載涯呻,Picasso不支持凉驻。
  • 緩存的區(qū)別
    glide內(nèi)存緩存和磁盤緩存都做了二級緩存,Picasso只有一步內(nèi)存緩存复罐,磁盤緩存還是OkHttp做的涝登。
  • Picasso對飛行模式做了單獨的監(jiān)聽,并對網(wǎng)絡(luò)狀態(tài)的234G做了監(jiān)聽效诅,根據(jù)不同的網(wǎng)絡(luò)設(shè)置不同的線程池核心數(shù)
  • Picasso對在滑動狀態(tài)的view不會去請求胀滚,只有當停止滑動的時候才會請求
  • Glide綁定了activity的生命周期,當activity銷毀則取消對應(yīng)的請求和回收資源避免內(nèi)存泄漏乱投。
  • Glide的默認圖片格式是RGB_565咽笼,Picasso則是ARGB_8888,所以Picasso加載的圖片緩存是Glide的2倍戚炫,但是清晰度比Glide高
  • Picasso會默認加載url對應(yīng)的全尺寸圖片剑刑,Glide則會根據(jù)imageview的大小緩存對應(yīng)尺寸的圖片,所以在內(nèi)存方面,Glide優(yōu)于Picasso
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末施掏,一起剝皮案震驚了整個濱河市钮惠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌七芭,老刑警劉巖素挽,帶你破解...
    沈念sama閱讀 222,946評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異狸驳,居然都是意外死亡毁菱,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,336評論 3 399
  • 文/潘曉璐 我一進店門锌历,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贮庞,“玉大人,你說我怎么就攤上這事究西〈吧鳎” “怎么了?”我有些...
    開封第一講書人閱讀 169,716評論 0 364
  • 文/不壞的土叔 我叫張陵卤材,是天一觀的道長遮斥。 經(jīng)常有香客問我,道長扇丛,這世上最難降的妖魔是什么术吗? 我笑而不...
    開封第一講書人閱讀 60,222評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮帆精,結(jié)果婚禮上较屿,老公的妹妹穿的比我還像新娘。我一直安慰自己卓练,他們只是感情好隘蝎,可當我...
    茶點故事閱讀 69,223評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著襟企,像睡著了一般嘱么。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上顽悼,一...
    開封第一講書人閱讀 52,807評論 1 314
  • 那天曼振,我揣著相機與錄音,去河邊找鬼蔚龙。 笑死冰评,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的府蛇。 我是一名探鬼主播集索,決...
    沈念sama閱讀 41,235評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了务荆?” 一聲冷哼從身側(cè)響起妆距,我...
    開封第一講書人閱讀 40,189評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎函匕,沒想到半個月后娱据,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,712評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡盅惜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,775評論 3 343
  • 正文 我和宋清朗相戀三年中剩,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抒寂。...
    茶點故事閱讀 40,926評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡结啼,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出屈芜,到底是詐尸還是另有隱情郊愧,我是刑警寧澤,帶...
    沈念sama閱讀 36,580評論 5 351
  • 正文 年R本政府宣布井佑,位于F島的核電站属铁,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏躬翁。R本人自食惡果不足惜焦蘑,卻給世界環(huán)境...
    茶點故事閱讀 42,259評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望盒发。 院中可真熱鬧例嘱,春花似錦、人聲如沸迹辐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,750評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽明吩。三九已至,卻和暖如春殷费,著一層夾襖步出監(jiān)牢的瞬間印荔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,867評論 1 274
  • 我被黑心中介騙來泰國打工详羡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留仍律,地道東北人。 一個月前我還...
    沈念sama閱讀 49,368評論 3 379
  • 正文 我出身青樓实柠,卻偏偏與公主長得像水泉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,930評論 2 361