Architecture(3)Picasso原始碼分析

wustor發表於2018-01-19

概述

前面分析了Volley的原始碼,現在來看一下Picasso的原始碼,其實Volley已經具備了載入了網路圖片的功能,只是效能不是很好,Picasso是Square公司推出的一款圖片載入框架,只能載入圖片,所以效能肯定會比Volley好,Picasso的很多設計實際上跟Volley很相似,後來看到Picasso中的一個類BitmapHunter的註釋,發現其實Picasso的作者也是參考了Volley的一些設計的。

Global lock for bitmap decoding to ensure that we are only are decoding one at a time. Since this will only ever happen in background threads we help avoid excessive memory thrashing as well as potential OOMs. Shamelessly stolen from Volley.
 
複製程式碼

當時看到註釋的最後一句就笑了,牛逼的框架也都是一步一步完善起來的,不過很佩服Picasso的作者,還在註釋中說出來,也是沒誰了,下面總結一下Picasso的設計原理。

Picasso

Picasso大致的可以分這幾大塊,其實也比較好理解,如果我們只是載入一張圖片,那麼很自然的可以連工具類都不用寫,用HttpUrlConnection或者用OkhttoUrlConnection去讀流,然後進行轉碼,適當的比例縮放就可以載入一張圖片,框架無非就是為了提高程式碼的複用性以及載入速度,載入效能,不然也不會有人去寫去用,對於Bitmap,有一點需要注意的就是OOM,然後結合以上節點,就是大部分框架需要考慮的東西。

正文

載入流程

先來看Picasso是如何呼叫的

Picasso.get()//獲取Picasso單例
  .load("url")//RequestCreator配置url,返回RequestCreator
  .placeholder(R.mipmap.ic_launcher)//RequestCreator配置佔點陣圖,返回RequestCreator
  .error(R.mipmap.ic_launcher)//RequestCreator配置失敗的佔點陣圖,返回RequestCreator
  .fit()//自動縮放圖片,返回RequestCreator
  .into(new ImageView(this));//開始載入圖片
複製程式碼

這個是在Github上面的最新的demo裡面,已經用get代替了之前的with方法這裡並沒有去畫流程圖或者類圖,感覺太大必要,因為自己寫過簡單的圖片載入框架,前面也分析過Volley的原始碼,整體的流程都是大同小異的,對於Picasso而言,依然是先配置一些基本資訊,快取策略,佔點陣圖等,然後就開始根據快取策略進行資料讀取,所以,不再贅述,打算就Picasso的一些核心類進行重點分析。

get方法

static volatile Picasso singleton = null;
public static Picasso get() {
    if (singleton == null) {
        synchronized (Picasso.class) {
            http:
            if (singleton == null) {
                if (PicassoProvider.context == null) {
                    throw new IllegalStateException("context == null");
                }
                singleton = new Builder(PicassoProvider.context).build();
            }
        }
    }
    return singleton;
}
複製程式碼

由於此方法是會在多執行緒中經常被呼叫的,所以為了保證執行緒安全,採用了雙重檢查,其實我們也可以用靜態內部類的形式來替代如下:

private static class NestedPicasso {
    public static Picasso singleton;
    static {
        System.out.println("instance = new SingletonTest()");
        if (PicassoProvider.context == null) {
            throw new IllegalStateException("context == null");
        }
        singleton = new Builder(PicassoProvider.context).build();
    }
}
public static Picasso get() {
    return NestedPicasso.singleton;
}
複製程式碼

into

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) {
    //fit操作跟resize不能同時存在,這個也很好理解,
    //要麼根據ImageView自己的尺寸要麼根據我傳入的尺寸,魚跟熊掌不可兼得
    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 || target.isLayoutRequested()) {
      //如果ImageView的寬高有一個為0,也就是wrap的情況那麼就必須延遲載入
      if (setPlaceholder) {
        //設定佔點陣圖
        setPlaceholder(target, getPlaceholderDrawable());
      }
      // 在DeferredRequestCreator重新測量
      picasso.defer(target, new DeferredRequestCreator(this, target, callback));
      return;
    }
    //對重新設定ImageView的尺寸
    data.resize(width, height);
  }
  Request request = createRequest(started);//建立一個請求
  String requestKey = createKey(request);//建立快取的key
  if (shouldReadFromMemoryCache(memoryPolicy)) {
    //如果需要從快取中讀取,則查詢記憶體快取
    Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
    if (bitmap != null) {
      //獲取到快取,取消掉請求
      picasso.cancelRequest(target);
      //設定Bitmap給ImageView
     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());
  }
//建立ImageViewAction
  Action action = new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,errorDrawable, requestKey, tag, callback, noFade);
//Action入隊
  picasso.enqueueAndSubmit(action);
}
複製程式碼

enqueueAndSubmit之後經過一系列呼叫,最終呼叫了Diapatcher的performSubmit

performSubmit

void performSubmit(Action action, boolean dismissFailed) {
  //如果之前是pause過,那麼呼叫resume
    if (pausedTags.contains(action.getTag())) {
        pausedActions.put(action.getTarget(), action);
        return;
    }
   //通過Key獲取BitmapHunter
    BitmapHunter hunter = hunterMap.get(action.getKey());
    if (hunter != null) {
      //將action新增進BitmapHunter
        hunter.attach(action);
        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);
   //拿到執行緒池執行的結果,BitmapHunter肯定是個Runnable或者Future
    hunter.future = service.submit(hunter);
  	//存入map中
    hunterMap.put(action.getKey(), hunter);
    if (dismissFailed) {
        failedActions.remove(action.getTarget());
    }
    if (action.getPicasso().loggingEnabled) {
        log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
    }
}
複製程式碼

我們知道把BitmapHunter放進了執行緒池,而BitmapHunter是個Runnable,那麼所有的耗時操作肯定是放在run方法,追蹤之後執行的是hunt方法,那麼就來看一下hunt的具體流程

hunt

Bitmap hunt() throws IOException {
  Bitmap bitmap = null;
  if (shouldReadFromMemoryCache(memoryPolicy)) {
    //再次讀取記憶體快取,因為不同的請求優先順序不一樣,很可能等到真正執行的時候
    //當前的url已經被快取了
    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 {
          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) {
        //通過上鎖,每次只有一個進行Decodeing
        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 (bitmap != null) {
        stats.dispatchBitmapTransformed(bitmap);
      }
    }
  }
  return bitmap;
}
複製程式碼

接下來的就是將Bitmap回傳給ImageView了,還是在Dispatcher中,呼叫了performRequest方法

void performComplete(BitmapHunter hunter) {
    if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
        cache.set(hunter.getKey(), hunter.getResult());
    }
    hunterMap.remove(hunter.getKey());
    batch(hunter);//追蹤Bitmap
 
}
複製程式碼

拿到資料,在這裡還是在工作執行緒裡面,由於需要在主執行緒進行顯示ImageView,所以需要切換執行緒,這裡用到了 初始化的Handler,也就是通過MainLooper初始化

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);
    }
}
複製程式碼

主執行緒拿到傳遞過來的訊息之後,經過中轉,最後還是到了ImageViewAction中,最終呼叫了complete方法

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;
  //設定Bitmap
  PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
  if (callback != null) {
    callback.onSuccess();
  }
}
複製程式碼

繼續看setBitmap

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);
  //最終設定的是一個BitmapDrawable
  target.setImageDrawable(drawable);
}

複製程式碼

所以最終設定給ImageView的不是Bitmap,而是一個BitmapDrawable,我們看一下setImagBitmap的原始碼

public void setImageBitmap(Bitmap bm) {
    // Hacky fix to force setImageDrawable to do a full setImageDrawable
    // instead of doing an object reference comparison
    mDrawable = null;
    if (mRecycleableBitmapDrawable == null) {
        mRecycleableBitmapDrawable = new BitmapDrawable(mContext.getResources(), bm);
    } else {
        mRecycleableBitmapDrawable.setBitmap(bm);
    }
    //也是呼叫了setImageDrawable
    setImageDrawable(mRecycleableBitmapDrawable);
}
複製程式碼

之所以設定的是BitmapDrawable,有兩個原因,一個是為了展示載入的動畫,另一個是在debug模式下面可以繪製右上角的三角除錯符號,這些都是在初始化BitmapDrawable的時候加進去的。

通過追蹤原始碼的方式,可以瞭解到Picasso整體載入流程,有很多細節沒有分析到,不過我們已經知道了,在分析過程中涉及到了很多類,其中有幾個類是反覆出現的,Picasso,RequestCreator,Dispatcher,RequestHandler,Action,另外還有一些類,比如說PicassoDrawable,LruCache,OkHttp3Downloader等,下面重點分析一下。

Picasso

成員變數

//清理執行緒,主要是用來清理ImageView被回收了的Request
private final CleanupThread cleanupThread;
//存放RequestHandler的集合
private final List<RequestHandler> requestHandlers;
//分發的一個關鍵類,主要用來轉發各種請求結果
final Dispatcher dispatcher;
//快取
final Cache cache;
//存放不同Action的Map
final Map<Object, Action> targetToAction;
//存放延遲載入的RequestCreator,也就是fit狀態下獲取的寬或者為高的ImageView
final Map<ImageView, DeferredRequestCreator> targetToDeferredRequestCreator;
//引用佇列,每隔一段時間獲取被回收的target
final ReferenceQueue<Object> referenceQueue;
複製程式碼

Picasso這個類持有了成員變數requestHandler集合,Action集合,Dispatcher,Cache這幾個前面提到的核心類,所以它自己實際上沒有什麼特殊的功能,只是在間接的呼叫了這些核心類的功能,實際上是一個裝飾者模式,暴露給外部呼叫者呼叫,這也是為什麼Picasso使用起來很簡潔,原因就在於Picasso在這個類,封裝了所有的方法。

構造方法

Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
        RequestTransformer requestTransformer, 
        List<RequestHandler> extraRequestHandlers, 
        Stats stats,Bitmap.Config defaultBitmapConfig, 
        boolean indicatorsEnabled, 
        boolean loggingEnabled) {
    this.context = context;
    this.dispatcher = dispatcher;
    this.cache = cache;
    this.listener = listener;
    this.requestTransformer = requestTransformer;
    this.defaultBitmapConfig = defaultBitmapConfig;
    int builtInHandlers = 7; 
    int extraCount = (extraRequestHandlers != null ? extraRequestHandlers.size() : 0);
    List<RequestHandler> allRequestHandlers = new ArrayList<>(builtInHandlers + extraCount);
    allRequestHandlers.add(new ResourceRequestHandler(context));
    if (extraRequestHandlers != null) {
        allRequestHandlers.addAll(extraRequestHandlers);
    }
    //初始化所有的圖片讀取Handler
    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);
     this.targetToAction = new WeakHashMap<>();//軟引用的Map
     this.targetToDeferredRequestCreator = new WeakHashMap<>();//軟引用的Map
    
  
}
複製程式碼

Builder

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;
    
    //構造Builder物件
    public Builder(@NonNull Context context) {
        if (context == null) {
            throw new IllegalArgumentException("Context must not be null.");
        }
        this.context = context.getApplicationContext();
    }
	//配置預設資訊
    public Builder defaultBitmapConfig(@NonNull Bitmap.Config bitmapConfig) {
        if (bitmapConfig == null) {
            throw new IllegalArgumentException("Bitmap config must not be null.");
        }
        this.defaultBitmapConfig = bitmapConfig;
        return this;
    }
	//配置下載器
    public Builder downloader(@NonNull Downloader downloader) {
        if (downloader == null) {
            throw new IllegalArgumentException("Downloader must not be null.");
        }
        if (this.downloader != null) {
            throw new IllegalStateException("Downloader already set.");
        }
        this.downloader = downloader;
        return this;
    }
     //配置執行緒池
    public Builder executor(@NonNull ExecutorService executorService) {
        if (executorService == null) {
            throw new IllegalArgumentException("Executor service must not be null.");
        }
        if (this.service != null) {
            throw new IllegalStateException("Executor service already set.");
        }
        this.service = executorService;
        return this;
    }
    //配置記憶體快取策略
    public Builder memoryCache(@NonNull Cache memoryCache) {
        if (memoryCache == null) {
            throw new IllegalArgumentException("Memory cache must not be null.");
        }
        if (this.cache != null) {
            throw new IllegalStateException("Memory cache already set.");
        }
        this.cache = memoryCache;
        return this;
    }
    //配置回撥接庫
    public Builder listener(@NonNull Listener listener) {
        if (listener == null) {
            throw new IllegalArgumentException("Listener must not be null.");
        }
        if (this.listener != null) {
            throw new IllegalStateException("Listener already set.");
        }
        this.listener = listener;
        return this;
    }

   //請求轉換器
    public Builder requestTransformer(@NonNull RequestTransformer transformer) {
        if (transformer == null) {
            throw new IllegalArgumentException("Transformer must not be null.");
        }
        if (this.transformer != null) {
            throw new IllegalStateException("Transformer already set.");
        }
        this.transformer = transformer;
        return this;
    }
  //新增請求處理器
    public Builder addRequestHandler(@NonNull RequestHandler requestHandler) {
        if (requestHandler == null) {
            throw new IllegalArgumentException("RequestHandler must not be null.");
        }
        if (requestHandlers == null) {
            requestHandlers = new ArrayList<>();
        }
        if (requestHandlers.contains(requestHandler)) {
            throw new IllegalStateException("RequestHandler already registered.");
        }
        requestHandlers.add(requestHandler);
        return this;
    }

   //指示器開關
    public Builder indicatorsEnabled(boolean enabled) {
        this.indicatorsEnabled = enabled;
        return this;
    }

    //日誌開關
    public Builder loggingEnabled(boolean enabled) {
        this.loggingEnabled = enabled;
        return this;
    }
  //構造Picasso
    public Picasso build() {
        Context context = this.context;
        if (downloader == null) {
            downloader = new OkHttp3Downloader(context);
        }
        if (cache == null) {
            cache = new LruCache(context);
        }
        if (service == null) {
            service = new PicassoExecutorService();
        }
        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);
    }
}
複製程式碼

由於Picasso的配置資訊很多,所以採用的是Builder模式,所以跟大多數框架一樣,採用了建造者模式進行構造的。

cancelTag

通過tag取消請求

public void cancelTag(@NonNull Object tag) {
    checkMain();
    if (tag == null) {
        throw new IllegalArgumentException("Cannot cancel requests with null tag.");
    }
    List<Action> actions = new ArrayList<>(targetToAction.values());
  //遍歷所有的Action,如果找到該tag,則進行取消
    for (int i = 0, n = actions.size(); i < n; i++) {
        Action action = actions.get(i);
        if (tag.equals(action.getTag())) {
          //取消請求
            cancelExistingRequest(action.getTarget());
        }
    }
    List<DeferredRequestCreator> deferredRequestCreators =
            new ArrayList<>(targetToDeferredRequestCreator.values());
  //取消需要重新測量的deferredRequestCreator
    for (int i = 0, n = deferredRequestCreators.size(); i < n; i++) {
        DeferredRequestCreator deferredRequestCreator = deferredRequestCreators.get(i);
        if (tag.equals(deferredRequestCreator.getTag())) {
            deferredRequestCreator.cancel();
        }
    }
}
複製程式碼

繼續看cancelExistingRequest

void cancelExistingRequest(Object target) {
    checkMain();
    Action action = targetToAction.remove(target);
    if (action != null) {
      //取消Action
        action.cancel();
      //取消BitmapHunter中的Action,一會兒再看Action中分析
        dispatcher.dispatchCancel(action);
    }
    if (target instanceof ImageView) {
        ImageView targetImageView = (ImageView) target;
        DeferredRequestCreator deferredRequestCreator =
                targetToDeferredRequestCreator.remove(targetImageView);
        if (deferredRequestCreator != null) {
          //取消延遲載入的請求
            deferredRequestCreator.cancel();
        }
    }
}
複製程式碼

enqueueAndSubmit

void enqueueAndSubmit(Action action) {
    Object target = action.getTarget();
    if (target != null && targetToAction.get(target) != action) {
        cancelExistingRequest(target);
        targetToAction.put(target, action);
    }
  //提交請求
    submit(action);
}
複製程式碼

resumeAction

void resumeAction(Action action) {
    Bitmap bitmap = null;
    //從記憶體中進行讀取
    if (shouldReadFromMemoryCache(action.memoryPolicy)) {
        bitmap = quickMemoryCacheCheck(action.getKey());
    }
    if (bitmap != null) {
        // 記憶體讀取成功,完成整個流程
        deliverAction(bitmap, LoadedFrom.MEMORY, action, null);
    } else {
        // 重新加入佇列
        enqueueAndSubmit(action);
        if (loggingEnabled) {
            log(OWNER_MAIN, VERB_RESUMED, action.request.logId());
        }
    }
}
複製程式碼

handler

//主執行緒中建立
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;
                for (int i = 0, n = batch.size(); i < n; i++) {
                    BitmapHunter hunter = batch.get(i);
                    hunter.picasso.complete(hunter);
                }
                break;
            }
            //GC回收的ImageView
            case REQUEST_GCED: {
                Action action = (Action) msg.obj;
                action.picasso.cancelExistingRequest(action.getTarget());
                break;
            }
            //請求恢復
            case REQUEST_BATCH_RESUME:
                for (int i = 0, n = batch.size(); i < n; i++) {
                    Action action = batch.get(i);
                    action.picasso.resumeAction(action);
                }
                break;
            default:
                throw new AssertionError("Unknown handler message received: " + msg.what);
        }
    }
};
複製程式碼

load

載入的資源路徑

//載入路徑
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));
}

//載入資源id
public RequestCreator load(@DrawableRes int resourceId) {
    if (resourceId == 0) {
        throw new IllegalArgumentException("Resource ID must not be zero.");
    }
    return new RequestCreator(this, null, resourceId);
}
複製程式碼

load有很多過載方法,可以用來載入網路url,讀取檔案,讀取資源id,返回的是RequestCreator

pauseTag

暫停請求

 */
public void pauseTag(@NonNull Object tag) {
    if (tag == null) {
        throw new IllegalArgumentException("tag == null");
    }
   //diapatcher轉發
    dispatcher.dispatchPauseTag(tag);
}
複製程式碼

resumeTag

public void resumeTag(@NonNull Object tag) {
    if (tag == null) {
        throw new IllegalArgumentException("tag == null");
    }
  //diapatcher轉發
    dispatcher.dispatchResumeTag(tag);
}

複製程式碼

quickMemoryCacheCheck

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

complete

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) {
        //多個Action,依次遍歷轉發
        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);
    }
}

複製程式碼

shutDown

public void shutdown() {
    if (this == singleton) {
        throw new UnsupportedOperationException("Default singleton instance cannot be shutdown.");
    }
    if (shutdown) {
        return;
    }
 
    cache.clear();  //清空快取
    cleanupThread.shutdown();//清理引用佇列
    stats.shutdown();//關閉統計
    dispatcher.shutdown(); //關閉Dispatcher
    //取消所有延遲載入的請求
    for (DeferredRequestCreator deferredRequestCreator:targetToDeferredRequestCreator.values()) {
        deferredRequestCreator.cancel();
    }
    targetToDeferredRequestCreator.clear();
    shutdown = true;
}
複製程式碼

RequestHandler

繼承關係

RequestHandler

RequestHandler有很多子類,這些是實際上進行網路請求的處理器,使用最多的是NetworkRequestHandler,其核心方法是load,主要交給子類實現,看一下NetworkRequestHandler的實現:

@Override
public Result load(Request request, int networkPolicy) throws IOException {
    //採用最新版的Okhttp3進行網路請求
    okhttp3.Request downloaderRequest = createRequest(request, networkPolicy);
    //獲取網路返回的結果
    Response response = downloader.load(downloaderRequest);
    ResponseBody body = response.body();
	//省略若干程式碼
   //判斷資料結果是來源於網路還是磁碟
    Picasso.LoadedFrom loadedFrom = response.cacheResponse() == null ? NETWORK : DISK;
    return new Result(body.source(), loadedFrom);
}
複製程式碼

這裡其實有一點需要注意的就是,我們在Picasso包下並沒有發現磁碟快取,只有LruCache這個類,也就是通常所說的記憶體快取,但是並沒有磁碟快取,實際上Picasso的快取全部交給了OKhttp來實現了,預設的快取路徑是由Utils生成的,PICASSO_CACHE的值picasso-cache

static File createDefaultCacheDir(Context context) {
  File cache = new File(context.getApplicationContext().getCacheDir(), PICASSO_CACHE);
  if (!cache.exists()) {
    cache.mkdirs();
  }
  return cache;
}
複製程式碼

成員變數

private static final AtomicInteger nextId = new AtomicInteger();//請求ID
private final Picasso picasso;//Picasso引用
private final Request.Builder data;//builder構造物件
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;//請求的tag
複製程式碼

很簡單,除了Picasso之外,沒有持有其它核心類的引用

構造方法

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;
  //不管是傳入的什麼引數,Picasso最後都是通過Uri來進行構造的,這樣就需要針對不同的載入型別
  //來實現過載了
    this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
}
複製程式碼

Builder模式

引數Builder
//通過Uri構造
public Builder(@NonNull Uri uri) {
    setUri(uri);
}
//通過資源ID構造
public Builder(@DrawableRes int resourceId) {
    setResourceId(resourceId);
}
//同時傳入Uri,資源ID,BitmapConfig
Builder(Uri uri, int resourceId, Bitmap.Config bitmapConfig) {
    this.uri = uri;
    this.resourceId = resourceId;
    this.config = bitmapConfig;
}

複製程式碼
Request構造
private Builder(Request request) {
    uri = request.uri;
    resourceId = request.resourceId;
    stableKey = request.stableKey;
    targetWidth = request.targetWidth;
    targetHeight = request.targetHeight;
    centerCrop = request.centerCrop;
    centerInside = request.centerInside;
    centerCropGravity = request.centerCropGravity;
    rotationDegrees = request.rotationDegrees;
    rotationPivotX = request.rotationPivotX;
    rotationPivotY = request.rotationPivotY;
    hasRotationPivot = request.hasRotationPivot;
    purgeable = request.purgeable;
    onlyScaleDown = request.onlyScaleDown;
    if (request.transformations != null) {
        transformations = new ArrayList<>(request.transformations);
    }
    config = request.config;
    priority = request.priority;
}
複製程式碼
fit方法

自適應ImageView的尺寸

public RequestCreator fit() {
    deferred = true;
    return this;
}
複製程式碼
resize

重設尺寸

public Builder resize(@Px int targetWidth, @Px int targetHeight) {
    if (targetWidth < 0) {
        throw new IllegalArgumentException("Width must be positive number or 0.");
    }
    if (targetHeight < 0) {
        throw new IllegalArgumentException("Height must be positive number or 0.");
    }
    if (targetHeight == 0 && targetWidth == 0) {
        throw new IllegalArgumentException("At least one dimension has to be positive number.");
    }
    this.targetWidth = targetWidth;
    this.targetHeight = targetHeight;
    return this;
}
複製程式碼
屬性配置
public Builder centerCrop(int alignGravity) {
    if (centerInside) {
        throw new IllegalStateException("Center crop can not be used after calling centerInside");
    }
    centerCrop = true;
    centerCropGravity = alignGravity;
    return this;
}

public Builder clearCenterCrop() {
    centerCrop = false;
    centerCropGravity = Gravity.CENTER;
    return this;
}

public Builder centerInside() {
    if (centerCrop) {
        throw new IllegalStateException("Center inside can not be used after calling centerCrop");
    }
    centerInside = true;
    return this;
}

複製程式碼

Action

Action是一個包裝類,會對我們的載入行為進行一次包裝,包含了載入的資訊以及回撥,先看一下它的繼承關係

繼承關係

Action

Action跟RequestHandler一樣,有很多子類,不過很常見的一個是ImageViewAction,一會兒會重點分析一下

成員變數

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;
複製程式碼

包含了picasso,request以及target的弱引用

構造方法

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<>(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);
}
複製程式碼

下面看一下它的兩個方法,一個是complete一個是error方法,由於父類是抽象方法,所以只能交給子類去處理,所以下面以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.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
    if (callback != null) {
      //成功回撥
        callback.onSuccess();
    }
}
複製程式碼

error

@Override
public void error(Exception e) {
    ImageView target = this.target.get();
    if (target == null) {
        return;
    }
    Drawable placeholder = target.getDrawable();
   //如果的動畫還在繼續,那麼就停止動畫
    if (placeholder instanceof AnimationDrawable) {
        ((AnimationDrawable) placeholder).stop();
    }
  //如果設定載入失敗的佔點陣圖
    if (errorResId != 0) {
        target.setImageResource(errorResId);
    } else if (errorDrawable != null) {
     
        target.setImageDrawable(errorDrawable);
    }
    if (callback != null) {
      //失敗的回撥
        callback.onError(e);
    }
}
複製程式碼

實際上complete跟error的並沒有做什麼,都只是做了簡單的回撥,那麼也就是說Action只是一個包裝類,沒有額外的功能

BitmapHunter

Bitmap捕捉者,或者說是Bitmap獵人,就是用來尋找Bitmap的一個類,繼承自Runnable,說明可以用來進行耗時操作。

註釋

Global lock for bitmap decoding to ensure that we are only are decoding one at a time. Since this will only ever happen in background threads we help avoid excessive memory thrashing as well as potential OOMs. Shamelessly stolen from Volley.
複製程式碼

解碼Bitmap的全域性鎖用以保證我們每次只能夠解碼一個Bitmap。因為解碼操作只是發生在後臺執行緒,所以為了避免解碼時太多的記憶體佔用導致OOM,借鑑於Volley。

成員變數

final Picasso picasso;
final Dispatcher dispatcher;
final Cache cache;
final Stats stats;
final String key;
final Request data;
final int memoryPolicy;
int networkPolicy;
final RequestHandler requestHandler;
Action action;
List<Action> actions;
Bitmap result;
Future<?> future;
Picasso.LoadedFrom loadedFrom;
Exception exception;
int exifOrientation; // Determined during decoding of original resource.
int retryCount;
Picasso.Priority priority;
複製程式碼

持有所有核心類

構造方法

BitmapHunter(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action,RequestHandler requestHandler) {
    this.sequence = SEQUENCE_GENERATOR.incrementAndGet();
    this.picasso = picasso;
    this.dispatcher = dispatcher;
    this.cache = cache;
    this.stats = stats;
    this.action = action;
    this.key = action.getKey();
    this.data = action.getRequest();
    this.priority = action.getPriority();
    this.memoryPolicy = action.getMemoryPolicy();
    this.networkPolicy = action.getNetworkPolicy();
    this.requestHandler = requestHandler;
    this.retryCount = requestHandler.getRetryCount();
}
複製程式碼

可以看到,很多引數都是傳遞過來的,也就是自己不持有。

run

@Override
public void run() {
    try {
        updateThreadName(data);
        //獲取Bitmap
        result = hunt();
        if (result == null) {
            dispatcher.dispatchFailed(this);
        } else {
            dispatcher.dispatchComplete(this);
        }
    }
}
複製程式碼

hunt

Bitmap hunt() throws IOException {
  Bitmap bitmap = null;
  if (shouldReadFromMemoryCache(memoryPolicy)) {
    //再次讀取記憶體快取,因為不同的請求優先順序不一樣,很可能等到真正執行的時候
    //當前的url已經被快取了
    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 {
          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) {
        //通過上鎖,每次只有一個進行Decodeing
        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 (bitmap != null) {
        stats.dispatchBitmapTransformed(bitmap);
      }
    }
  }
  return bitmap;
}
複製程式碼

forRequest

static BitmapHunter forRequest(
  Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action) {
    Request request = action.getRequest();
    List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
   //遍歷已有的requestHandlers,看看能不能有能夠處理的requestHandler
    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);
}
複製程式碼

performSubmit

void performSubmit(Action action, boolean dismissFailed) {
  //如果之前是pause過,那麼呼叫resume
    if (pausedTags.contains(action.getTag())) {
        pausedActions.put(action.getTarget(), action);
        return;
    }
   //通過Key獲取BitmapHunter
    BitmapHunter hunter = hunterMap.get(action.getKey());
    if (hunter != null) {
      //將action新增進BitmapHunter
        hunter.attach(action);
        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);
   //拿到執行緒池執行的結果,BitmapHunter肯定是個Runnable或者Future
    hunter.future = service.submit(hunter);
  	//存入map中
    hunterMap.put(action.getKey(), hunter);
    if (dismissFailed) {
        failedActions.remove(action.getTarget());
    }
    if (action.getPicasso().loggingEnabled) {
        log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
    }
}
複製程式碼

performPauseTag

void performPauseTag(Object tag) {
    // 已經paused過,直接返回
    if (!pausedTags.add(tag)) {
        return;
    }
    // Go through all active hunters and detach/pause the requests
    // that have the paused tag.
    for (Iterator<BitmapHunter> it = hunterMap.values().iterator(); it.hasNext(); ) {
        BitmapHunter hunter = it.next();
        boolean loggingEnabled = hunter.getPicasso().loggingEnabled;
        Action single = hunter.getAction();
        List<Action> joined = hunter.getActions();
        boolean hasMultiple = joined != null && !joined.isEmpty();
        // Hunter has no requests, bail early.
        if (single == null && !hasMultiple) {
            continue;
        }
       //判斷當前唯一的Action是否跟暫停的tag相同
        if (single != null && single.getTag().equals(tag)) {
          //從hunter中移除
            hunter.detach(single);
          //新增進pausedActions
            pausedActions.put(single.getTarget(), single);
        }
        if (hasMultiple) {
          //如果有多個Action
            for (int i = joined.size() - 1; i >= 0; i--) {
                Action action = joined.get(i);
                if (!action.getTag().equals(tag)) {
                    continue;
                }
              //跟單個一樣
                hunter.detach(action);
                pausedActions.put(action.getTarget(), action);
            }
        }
    }
}
複製程式碼

performResumeTag

void performResumeTag(Object tag) {
    // 移除恢復的tag
    if (!pausedTags.remove(tag)) {
        return;
    }
    List<Action> batch = null;
    for (Iterator<Action> i = pausedActions.values().iterator(); i.hasNext(); ) {
        Action action = i.next();
        if (action.getTag().equals(tag)) {
            if (batch == null) {
                batch = new ArrayList<>();
            }
          //新增進List集合彙總
            batch.add(action);
          //從暫停的集合中移除
            i.remove();
        }
    }
    if (batch != null) {
      //切換到主執行緒,執行resumeAction
       mainThreadHandler.sendMessage(
         mainThreadHandler.obtainMessage(REQUEST_BATCH_RESUME, batch));
    }
}
複製程式碼

實際上performResumeTag在Picasso的分析中已經說明,如果沒有設定記憶體快取,那麼相當於重新開啟一個請求,如果設定了記憶體快取,讀取失敗也會重新開啟一個請求,只有在開啟記憶體快取並且讀取成功performResumeTag才不會重新建立請求。

decodeStream

static Bitmap decodeStream(Source source, Request request) throws IOException {
    BufferedSource bufferedSource = Okio.buffer(source);
    boolean isWebPFile = Utils.isWebPFile(bufferedSource);
    boolean isPurgeable = request.purgeable && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP;
    BitmapFactory.Options options = RequestHandler.createBitmapOptions(request);
    boolean calculateSize = RequestHandler.requiresInSampleSize(options);
    if (isWebPFile || isPurgeable) {
        byte[] bytes = bufferedSource.readByteArray();
        if (calculateSize) {
            BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
            RequestHandler.calculateInSampleSize(
              request.targetWidth, request.targetHeight, options,request);
        }
        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
    } else {
        InputStream stream = bufferedSource.inputStream();
        if (calculateSize) {
            // TODO use an InputStream that buffers with Okio...
            MarkableInputStream markStream = new MarkableInputStream(stream);
            stream = markStream;
            markStream.allowMarksToExpire(false);
            long mark = markStream.savePosition(1024);
            BitmapFactory.decodeStream(stream, null, options);
            //計算Bitmap的尺寸
            RequestHandler.calculateInSampleSize(
              request.targetWidth, request.targetHeight, options,request);
            markStream.reset(mark);
            markStream.allowMarksToExpire(true);
        }
        Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
        if (bitmap == null) {
            throw new IOException("Failed to decode stream.");
        }
        return bitmap;
    }
}
複製程式碼

calculateInSampleSize(RequestHandler的方法)

static void calculateInSampleSize(int reqWidth, int reqHeight, int width, int height,
    BitmapFactory.Options options, Request request) {
  int sampleSize = 1;
  if (height > reqHeight || width > reqWidth) {
    final int heightRatio;
    final int widthRatio;
    if (reqHeight == 0) {
      sampleSize = (int) Math.floor((float) width / (float) reqWidth);
    } else if (reqWidth == 0) {
      sampleSize = (int) Math.floor((float) height / (float) reqHeight);
    } else {
      heightRatio = (int) Math.floor((float) height / (float) reqHeight);
      widthRatio = (int) Math.floor((float) width / (float) reqWidth);
      sampleSize = request.centerInside
          ? Math.max(heightRatio, widthRatio)
          : Math.min(heightRatio, widthRatio);
    }
  }
  options.inSampleSize = sampleSize;
  options.inJustDecodeBounds = false;
}
複製程式碼

通過傳入目標寬高以及實際的寬高進行縮放,然後進行縮放得到想要的尺寸

Dispatcher

成員變數

final DispatcherThread dispatcherThread;//HandlerThread,用於在子執行緒中建立Looper
final Context context;
final ExecutorService service;//執行緒池
final Downloader downloader;//圖片下載器
final Map<String, BitmapHunter> hunterMap;//存放BitmapHunter
final Map<Object, Action> failedActions;//失敗的請求
final Map<Object, Action> pausedActions;//暫停的請求
final Set<Object> pausedTags;//暫停的tag集合
final Handler handler;//Dispatcher中訊息轉發的Handler
final Handler mainThreadHandler;//主執行緒中的Handler
final Cache cache;
final Stats stats;
final List<BitmapHunter> batch;//BitmapHunter集合
final NetworkBroadcastReceiver receiver;//網路狀態監聽的廣播
final boolean scansNetworkChanges;網路狀態是否切換的標誌
複製程式碼

構造方法

Dispatcher(Context context, ExecutorService service, Handler mainThreadHandler,
           Downloader downloader, Cache cache, Stats stats) {
    this.dispatcherThread = new DispatcherThread();
    this.dispatcherThread.start();//啟動HandlerThread
    Utils.flushStackLocalLeaks(dispatcherThread.getLooper());
    this.context = context;
    this.service = service;
    this.hunterMap = new LinkedHashMap<>();
    this.failedActions = new WeakHashMap<>();//軟引用
    this.pausedActions = new WeakHashMap<>();//軟引用
    this.pausedTags = new HashSet<>();
    this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
    this.downloader = downloader;
    this.mainThreadHandler = mainThreadHandler;
    this.cache = cache;
    this.stats = stats;
    this.batch = new ArrayList<>(4);
    this.airplaneMode = Utils.isAirplaneModeOn(this.context);
  //如果沒有獲取到網路狀態改變的許可權則全部為false
    this.scansNetworkChanges      =hasPermission(context,Manifest.permission.ACCESS_NETWORK_STATE);
  //建立網路狀態監聽的廣播
    this.receiver = new NetworkBroadcastReceiver(this);
  //註冊廣播
    receiver.register();
}
複製程式碼

Handler

這是區別於MainHandler的一個內部的Handler,用在這裡可以讓邏輯更加清晰,實際上不用也可以。

private static class DispatcherHandler extends Handler {
    private final Dispatcher dispatcher;
   //傳入子執行緒的Looper
    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;
            }
    }
}
複製程式碼

NetworkBroadcastReceiver

網路監聽的廣播

static class NetworkBroadcastReceiver extends BroadcastReceiver {
    static final String EXTRA_AIRPLANE_STATE = "state";
    private final Dispatcher dispatcher;
    NetworkBroadcastReceiver(Dispatcher dispatcher) {
        this.dispatcher = dispatcher;
    }
    void register() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION_AIRPLANE_MODE_CHANGED);
        if (dispatcher.scansNetworkChanges) {
            filter.addAction(CONNECTIVITY_ACTION);
        }
        dispatcher.context.registerReceiver(this, filter);
    }
    void unregister() {
        dispatcher.context.unregisterReceiver(this);
    }
    @SuppressLint("MissingPermission")
    @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; /
            }
         dispatcher.dispatchAirplaneModeChange(
           intent.getBooleanExtra(EXTRA_AIRPLANE_STATE, false));
        } else if (CONNECTIVITY_ACTION.equals(action)) {
      ConnectivityManager connectivityManager = getService(context, CONNECTIVITY_SERVICE);
          //將網路狀態的變化通知內部的Handler
       dispatcher.dispatchNetworkStateChange(connectivityManager.getActiveNetworkInfo());
        }
    }
}

複製程式碼

dispatchNetworkStateChange最後還是呼叫了adjustThreadCount

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);
  }
}
複製程式碼

也就是根據不同的網路狀態設定執行緒池的核心執行緒數以及最大執行緒數

performSubmit

void performSubmit(Action action, boolean dismissFailed) {
  //如果之前是pause過,那麼呼叫resume
    if (pausedTags.contains(action.getTag())) {
        pausedActions.put(action.getTarget(), action);
        return;
    }
   //通過Key獲取BitmapHunter
    BitmapHunter hunter = hunterMap.get(action.getKey());
    if (hunter != null) {
      //將action新增進BitmapHunter
        hunter.attach(action);
        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);
   //拿到執行緒池執行的結果,BitmapHunter肯定是個Runnable或者Future
    hunter.future = service.submit(hunter);
  	//存入map中
    hunterMap.put(action.getKey(), hunter);
    if (dismissFailed) {
        failedActions.remove(action.getTarget());
    }
    if (action.getPicasso().loggingEnabled) {
        log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
    }
}
複製程式碼

performCancel

void performCancel(Action action) {
    String key = action.getKey();
    //獲取hunter
    BitmapHunter hunter = hunterMap.get(key);
    if (hunter != null) {
      從hunter中移除這個Action
        hunter.detach(action);
        if (hunter.cancel()) {
            hunterMap.remove(key);
        }
    }
   //如果pausedTags中含有此Action,移除掉
    if (pausedTags.contains(action.getTag())) {
        pausedActions.remove(action.getTarget());
    }
   //嘗試著從失敗的Action中移除此Action
    Action remove = failedActions.remove(action.getTarget());
}
複製程式碼

shutdown

void shutdown() {
    // 關閉執行緒池
    if (service instanceof PicassoExecutorService) {
        service.shutdown();
    }
   //關閉下載器
    downloader.shutdown();
   //退出handler
    dispatcherThread.quit();
    //移除廣播
    Picasso.HANDLER.post(new Runnable() {
        @Override
        public void run() {
            receiver.unregister();
        }
    });
}
複製程式碼

總結

上面分析了Picasso的核心類,還有一些比較重要的類,LrucachePicassoExecutorService,由於Lrucache跟Volley的DiskBaseCache並無區別,底層採用LinkedHashMap,通過使用的順序來進行排序,容量不足時刪除最近最少使用的資料,而PicassoExecutorService屬於執行緒池,之前也分析過。

Picasso相對於Volley註釋太少,不是很好閱讀,所幸命名比較規範,可以根據名字大致猜出這個類是幹什麼的,整體封裝性比Volley好,但是擴充套件性不如Volley,不過都有很多值得學習的地方,下面簡單對比分析一下:

Volley Picasso
Request Request RequestCreator—>Request—>Action—>BitmapHunter
Cache DiskLruCache DiskLruCache&DiskDiskLruCache
RequstQueue BlockingQueue ThreadPoolExecutor
Dispatcher CacheDispatcher& NetworkDispatcher DispatcherThread
TaskAction cancel cancel,pause,resume

參考資料

blog.csdn.net/chdjj/artic…

blog.happyhls.me/category/an…

相關文章