Picasso源碼分析

一 介紹

Square公司開源的圖片加載庫。優(yōu)點(diǎn)是功能還算完善,能滿足基本的圖片加載需求,使用簡(jiǎn)單,體量輕易推倒。
官方鏈接:http://square.github.io/picasso/
Git: https://github.com/square/picasso

二 分析

1. 簡(jiǎn)單調(diào)用
Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
2. 流程圖
Paste_Image.png
3. 核心類分析

(主流程順序,以下貼出的代碼都是經(jīng)過篩選了比較重要的代碼)

①Picasso
-- 單例,構(gòu)造ExecutorService線程池(默認(rèn)3個(gè)線程),Dispatcher,LruCache,Downloader(調(diào)用okhttp),其他基本沒干什么正事。

//Builder模式構(gòu)建Picasso實(shí)例
public static class Builder {
  private final Context context;  
  private Downloader downloader;  
  private ExecutorService service;  
  private Cache cache;  
  private Listener listener;  
  private RequestTransformer transformer;  
  private List<RequestHandler> requestHandlers;  
  private Bitmap.Config defaultBitmapConfig;  
  private boolean indicatorsEnabled;  
  private boolean loggingEnabled;  
  /** Start building a new {@link Picasso} instance. */ 
  public Builder(Context context) {   
     if (context == null) {         
        throw new IllegalArgumentException("Context must not be null.");  
     }
     this.context = context.getApplicationContext();  
  }
  ....//省略以下代碼
}

②RequestCreator
-- 主流程調(diào)用的方法into(), 里面做的最重要的兩件事:1.創(chuàng)建Request, 2.把Action加到Dispatcher隊(duì)列。(Request, Action, Dispatcher幾個(gè)概念后面會(huì)做說明)

public void into(ImageView target, Callback callback) {
   
    Request request = createRequest(started);
    String requestKey = createKey(request);
    //查看內(nèi)存緩存是否已經(jīng)有了,有的話直接set,不用再去構(gòu)造Action獲取了
    if (shouldReadFromMemoryCache(memoryPolicy)) {
      Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
      if (bitmap != null) {
        picasso.cancelRequest(target);
        setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
        if (callback != null) {
          callback.onSuccess();
        }
        return;
      }
    }
  
    if (setPlaceholder) {
      setPlaceholder(target, getPlaceholderDrawable());
    }
    //構(gòu)造Action
    Action action =
        new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
            errorDrawable, requestKey, tag, callback, noFade);
    //扔給任務(wù)調(diào)度器
    picasso.enqueueAndSubmit(action);
  }

③Request
-- 請(qǐng)求model,存儲(chǔ)請(qǐng)求相關(guān)的數(shù)據(jù),包括Transformation list,部分內(nèi)置的轉(zhuǎn)換屬性,Bitmap壓縮屬性, 任務(wù)優(yōu)先級(jí), 圖片資源的相關(guān)信息等。

//類結(jié)構(gòu),原生注釋很詳細(xì)
public final class Request {
  private static final long TOO_LONG_LOG = TimeUnit.SECONDS.toNanos(5);

  /** A unique ID for the request. */
  int id;
  /** The time that the request was first submitted (in nanos). */
  long started;
  /** The {@link NetworkPolicy} to use for this request. */
  int networkPolicy;

  /**
   * The image URI.
   * <p>
   * This is mutually exclusive with {@link #resourceId}.
   */
  public final Uri uri;
  /**
   * The image resource ID.
   * <p>
   * This is mutually exclusive with {@link #uri}.
   */
  public final int resourceId;
  /**
   * Optional stable key for this request to be used instead of the URI or resource ID when
   * caching. Two requests with the same value are considered to be for the same resource.
   */
  public final String stableKey;
  /** List of custom transformations to be applied after the built-in transformations. */
  public final List<Transformation> transformations;
  /** Target image width for resizing. */
  public final int targetWidth;
  /** Target image height for resizing. */
  public final int targetHeight;
  /**
   * True if the final image should use the 'centerCrop' scale technique.
   * <p>
   * This is mutually exclusive with {@link #centerInside}.
   */
  public final boolean centerCrop;
  /**
   * True if the final image should use the 'centerInside' scale technique.
   * <p>
   * This is mutually exclusive with {@link #centerCrop}.
   */
  public final boolean centerInside;
  public final boolean onlyScaleDown;
  /** Amount to rotate the image in degrees. */
  public final float rotationDegrees;
  /** Rotation pivot on the X axis. */
  public final float rotationPivotX;
  /** Rotation pivot on the Y axis. */
  public final float rotationPivotY;
  /** Whether or not {@link #rotationPivotX} and {@link #rotationPivotY} are set. */
  public final boolean hasRotationPivot;
  /** True if image should be decoded with inPurgeable and inInputShareable. */
  public final boolean purgeable;
  /** Target image config for decoding. */
  public final Bitmap.Config config;
  /** The priority of this request. */
  public final Priority priority;
....//省略以下代碼

④Action
-- 可以看做單個(gè)圖片加載的任務(wù)model,抽象類。
實(shí)現(xiàn)類可見下圖,常用的是ImageViewAction,它會(huì)在抽象方法complete真正實(shí)現(xiàn)把Bitmap給ImageView的行為。


Paste_Image.png
abstract class Action<T> {
  final Picasso picasso;  //picasso單例
  final Request request;  //當(dāng)前任務(wù)的request
  final WeakReference<T> target;  //目標(biāo),imageview
  final boolean noFade;  //是否漸進(jìn)漸出
  final int memoryPolicy;  //內(nèi)存緩存策略
  final int networkPolicy;  //網(wǎng)絡(luò)緩存策略
  final int errorResId;  //錯(cuò)誤占位圖資源id
  final Drawable errorDrawable;  //錯(cuò)誤占位圖資源drawable
  final String key; //內(nèi)存緩存的key
  final Object tag; //dispatcher的任務(wù)標(biāo)識(shí)

  boolean willReplay;  //是否再次放到任務(wù)隊(duì)列中
  boolean cancelled;  //是否取消

  //Bitmap加載成功回調(diào)
  abstract void complete(Bitmap result, Picasso.LoadedFrom from);
  //加載失敗回調(diào)
  abstract void error();
....//省略以下代碼

⑤Dispatcher
-- 看名字就看出來是個(gè)任務(wù)分發(fā)調(diào)度器,

dispatcher方法流程:

Paste_Image.png
class Dispatcher {
  
  final DispatcherThread dispatcherThread;  //dispatcher線程,主要作用是獲取looper給handler用
  final Context context;  
  final ExecutorService service;  //picasso創(chuàng)建的線程池,唯一
  final Downloader downloader;  //下載器,唯一
  final Map<String, BitmapHunter> hunterMap;  //所有的BitmapHunter map
  final Map<Object, Action> failedActions;
  final Map<Object, Action> pausedActions;
  final Set<Object> pausedTags;
  final Handler handler;  //dispatcher線程handler,任務(wù)調(diào)用使用
  final Handler mainThreadHandler;  //主線程handler,用于complete后的行為
  final Cache cache;  //內(nèi)存緩存
  final Stats stats;
  final List<BitmapHunter> batch;  //批量complete狀態(tài)的BitmapHunter
  final NetworkBroadcastReceiver receiver; //監(jiān)控網(wǎng)絡(luò)狀態(tài),用于網(wǎng)絡(luò)緩存策略
  
  //從Action構(gòu)造BitmapHunter并且放到線程池中
  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;
    }

    //構(gòu)造request
    hunter = forRequest(action.getPicasso(), this, cache, stats, action);
   //執(zhí)行hunter
    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());
    }
  }
  //單個(gè)任務(wù)完成 ,batch聚合成功任務(wù)結(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");
    }
  }
  //給picasso主線程處理批量結(jié)果
  void performBatchComplete() {
    List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
    batch.clear();
    mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
    logBatch(copy);
  }

⑥BitmapHunter
-- 比較簡(jiǎn)單,實(shí)現(xiàn)了Runnable接口, 重點(diǎn)關(guān)注hunt()方法。

Bitmap hunt() throws IOException {
    Bitmap bitmap = null;
   //省略從內(nèi)存讀bitmap代碼...

    //從requestHandler 同步load result,RequestHandler見后面分析
    RequestHandler.Result result = requestHandler.load(data, networkPolicy);
    if (result != null) {
      bitmap = result.getBitmap();
      // If there was no Bitmap then we need to decode it from the stream.
      if (bitmap == null) {
        InputStream is = result.getStream();
        bitmap = decodeStream(is, data);
      }
    }

    if (bitmap != null) {
      //如果需要做內(nèi)置的轉(zhuǎn)換,則轉(zhuǎn)出相應(yīng)的bitmap
      if (data.needsTransformation() || exifOrientation != 0) {
            bitmap = transformResult(data, bitmap, exifOrientation);
          }
      //如果需要做自定義的轉(zhuǎn)換,則轉(zhuǎn)出相應(yīng)的bitmap
          if (data.hasCustomTransformations()) {
            bitmap = applyCustomTransformations(data.transformations, bitmap);
            }
          }
        }
      }
    }
    return bitmap;
  }

⑦RequestHandler
-- 抽象類,抽象方法load() 方法獲取Result, Result里有bitmap和inputstream

實(shí)現(xiàn)類:


Paste_Image.png

我們來看下最常用的NetworkRequestHandler

class NetworkRequestHandler extends RequestHandler {
  //根據(jù)request加載出bitmap或者inputstream
  @Override public Result load(Request request, int networkPolicy) throws IOException {
   
    //走Downloader請(qǐng)求,downloader內(nèi)部是okhttp實(shí)現(xiàn)
    Response response = downloader.load(request.uri, request.networkPolicy);

    Bitmap bitmap = response.getBitmap();
    if (bitmap != null) {
      return new Result(bitmap, loadedFrom);
    }

    InputStream is = response.getInputStream();
    if (is == null) {
      return null;
    }
  }

BitmapHunter里L(fēng)ist<RequestHandler>是在picasso的構(gòu)造方法中注入的。

Picasso類相關(guān)代碼:

    List<RequestHandler> allRequestHandlers =
        new ArrayList<RequestHandler>(builtInHandlers + extraCount);

    allRequestHandlers.add(new ResourceRequestHandler(context));
    if (extraRequestHandlers != null) {
      allRequestHandlers.addAll(extraRequestHandlers);
    }
    allRequestHandlers.add(new ContactsPhotoRequestHandler(context));
    allRequestHandlers.add(new MediaStoreRequestHandler(context));
    allRequestHandlers.add(new ContentStreamRequestHandler(context));
    allRequestHandlers.add(new AssetRequestHandler(context));
    allRequestHandlers.add(new FileRequestHandler(context));
    allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
    requestHandlers = Collections.unmodifiableList(allRequestHandlers);

BitmapHunter類相關(guān)代碼:

    List<RequestHandler> requestHandlers = picasso.getRequestHandlers();

    for (int i = 0, count = requestHandlers.size(); i < count; i++) {
      RequestHandler requestHandler = requestHandlers.get(i);
      //責(zé)任鏈模式,第一個(gè)可用的handler直接使用
      if (requestHandler.canHandleRequest(request)) {
        return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
      }
    }

三 內(nèi)部機(jī)制及優(yōu)缺點(diǎn)

1、提供了雙緩存,Memory緩存使用LinkedHashMap實(shí)現(xiàn),提供命中率統(tǒng)計(jì)。
Disk緩存使用的是okhttp內(nèi)部的,無法設(shè)置其他的Disk緩存,要想實(shí)現(xiàn)只能自己改造代碼了。(OkHttpDownloader和OkHttp3Downloader)
2、自定制Bitmap轉(zhuǎn)換,需要繼承Transformation接口,在構(gòu)造RequestCreator的時(shí)候add進(jìn)去。類似切圓角,高斯模糊等需求都在這里完成。Picasso本身提供了Resize,CenterCrop,Rotate,CenterInside轉(zhuǎn)換方式。
3、圖片渲染只提供了fade方式, 見PicassoDrawable。
4、可load的圖片資源種類:Resource,Asset, File, Http, (Https之前版本不支持,從最新依賴的okhttp 3.0.1看應(yīng)該是支持的,未測(cè)試過)
5、不支持Gif, 支持webp。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 概述 在Android開發(fā)界,大家都知到square出品必屬精品,圖片加載庫Picasso自然也是。本文就從源碼角...
    朔野閱讀 768評(píng)論 0 7
  • Picasso,看的版本是v.2.5.2 使用方法,大概這么幾種加載資源的形式 還可以對(duì)圖片進(jìn)行一些操作:設(shè)置大小...
    Jinjins1129閱讀 420評(píng)論 0 3
  • Picasso是一個(gè)由Square公司開源的強(qiáng)大的Android圖片下載緩存庫,僅僅只需要一行代碼就可以實(shí)現(xiàn)圖片的...
    mishuai閱讀 389評(píng)論 0 0
  • 前一篇文章講了Picasso的詳細(xì)用法,Picasso 是一個(gè)強(qiáng)大的圖片加載緩存框架,一個(gè)非常優(yōu)秀的開源庫,學(xué)習(xí)一...
    依然范特稀西閱讀 4,747評(píng)論 13 24
  • 一個(gè)月前,被小皇表妹拉入坑,然后就沉迷亡者農(nóng)藥,日漸消瘦。 從剛開始的青銅渣,走在王者峽谷都會(huì)迷路,到現(xiàn)在能認(rèn)出每...
    葉老師機(jī)閱讀 2,689評(píng)論 7 2

友情鏈接更多精彩內(nèi)容