頻道欄目
首頁 > 資訊 > 其他 > 正文

Picasso源碼的簡單解析(一)

16-07-30        來源:[db:作者]  
收藏   我要投稿

前段時間簡單的分析了一下ImageLoader的源碼,所以就想看看使用很火的一些其他的圖片加載庫的實現,跟ImageLoader對比起來有什么優缺點。所以本系列的幾篇博文會時不時跟ImageLoader來個簡單的對比來說明問題。閑言少敘,開始Picasso分析之旅吧。
本篇只是簡單的對流程進行梳理,因為Picasso自己分析起來篇幅只多不少,所以本篇就簡單的說明。具體的某個知識點的細節會另外開篇博客說明之。
其實既然是緩存,肯定核心也就是那么幾點:memory cache,disk cache等等,總體上來說只是不同的庫對它們處理的方式不同而已。
當年老毛同志怎么說的來著:”要從戰略上藐視對方,從戰術上重視對方“。所以為了減少對閱讀源碼的恐懼感,讓我先從戰略上藐視一下picasso吧。剛把這玩意的源碼下載來一瞅,第一個感覺就是:我滴個乖乖,居然就一個包!神馬各種Action、各種RequestHandler統統特么的一股腦的放在了一個包下面,感覺就像一個潔白的床上堆滿了臭襪子、毛巾、衣服和拖鞋一樣亂糟糟的。就算你這個作者是技術大神,也不能組織類的時候這么不修邊幅!
好了,戰略藐視完畢,還是從戰術上來仔細的分析吧!

Picasso的總體流程:

總的來說Picasso的流程很簡單,當Picasso通過load方法獲取圖片的時候,需要經過如下步驟才能完成顯示圖片的流程:
1)將請求封裝為Request對象,然后將Request對象進一步封裝為Action(ImageAction)對象。
2)將Action(ImageAction)對象交給Dispather進行分發
3)最終交給BitmapHunter這個Runnable作為在線程池中線程的工作的單元
4)由RequestHandler來處理最終的請,將加載完成后的圖片交給PicassoDrawable顯示圖片。
代碼流程如下:Picasso->load->創建request->創建action->Dispatcher分發action->RequestHandler處理具體的請求->PicassoDrawable顯示圖片。

上面去掉了許多的枝枝蔓蔓簡單的說了一些流程,算是一個總綱吧,下面就圍繞著這個總綱來說明Picasso的具體細節。

Picasso對象的初始化

跟ImageLoaderConfiguration一樣,Picasso也是利用了Builder模式來組建Picasso,用Builder模式的好處之一就是可以通過Builder來清晰的知道Picasso可都可以提供哪些對外的配置接口供我們使用,同時我們自己在客戶端配置這些組件的話,Builder也可以提供默認的組件來使用。就讓我們看看Picasso的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 requestHandlers;
    private Bitmap.Config defaultBitmapConfig;

    private boolean indicatorsEnabled;
    private boolean loggingEnabled;

先不說這些Builder怎么使用它們,事實上Picasso的with方法來初始化一個Picasso單例對象,事實上就是用的Builder默認的配置來build一個Picasso對象出來:

  static volatile Picasso singleton = null;
public static Picasso with(@NonNull Context context) {
     。。。
    if (singleton == null) {
      synchronized (Picasso.class) {
        if (singleton == null) {
           //通過Builder默認的配置來創建一個Picasso
          singleton = new Builder(context).build();
        }
      }
    }
    return singleton;
  }

那么這個默認的Builder在build的時候都做了些神馬呢,其實不用多想也應該知道了,就是配置了諸如默認的下載器,請求轉換器,默認的memory cache等:

 public Picasso build() {
      Context context = this.context;
      if (downloader == null) {//客戶端沒有配置自己的Downloader
        downloader = Utils.createDefaultDownloader(context);
      }
      if (cache == null) {//客戶端沒有配置自己的memory cahce
        cache = new LruCache(context);//默認是LRU算法的memory cache
      }
      if (service == null) {//客戶端沒有配置自己的線程池
        service = new PicassoExecutorService();
      }
      if (transformer == null) {//客戶端沒有配置自己的轉換器
        //配置默認的請求轉換器,默認是對最初的Request不做轉換
        transformer = RequestTransformer.IDENTITY;
      }
      。。。此處暫時省略了重要的代碼。。。
    }
  }

我們知道Picasso中通過with方法其實是用默認的builder配置來初始化Picasso的單例對象。其實在你調用with方法之前(注意是調用with方法之前),還可以調用setSingletonInstance(Picasso)結合Builder配置自己的單例Picasso對象:

  static volatile Picasso singleton = null;
public static void setSingletonInstance(@NonNull Picasso picasso) {
   ......
    synchronized (Picasso.class) {
      if (singleton != null) {//這個非null判斷就意味著setSingletonInstance必須在with方法之前調用
        throw new IllegalStateException("Singleton instance already exists.");
      }
      //初始化單例對象
      singleton = picasso;
    }
  }

這其實也算是單例模式+Builder模式的靈活應用吧!挺值得學習、借鑒!

load+into的簡單說明

跟ImageLoader相同,Picasso也提供了對不同 圖片來源(比如assets里面的圖片,Filie里面的圖片,或者Drawable)的加載處理。不同之處就是ImageLoader對外提供了統一的顯示方法,然后由Imageloader自己判斷來源從而提供不同的流來獲取圖片,而Picasso提供了多個重載load方法來處理對應的情況:
這里寫圖片描述
我們就挑揀其中的一個load(Uri)來進行分析吧。
其實,Picasso的源碼分析起來也很簡單,層層跟進load方法調用路徑就可以了。
在調用load的時候實際上返回的是一個RequestCreator:

//request的builder對象,此處感覺應該命名為requestBuilder好點
  private final Request.Builder data;
  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;
    //通過構造器初始化request.Builder,并把uri傳給builder
    this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
  }

也就是說在調用load方法的時候實際上并沒有對圖片資源進行加載,只是簡單返回了一個RequestCreator對象,該對象在初始化的時候初始化了Request的Builder對象(好吧,又是Builder模式的應用)。
使用過Picasso的都知道,我們調用RequestCreator的into方法來完成工作的,那么就先簡單的分析RequestCreator的into系列重載方法之一進行說明:

public void into(ImageView target, Callback callback) {
    long started = System.nanoTime();
    //判斷是否是主線程
    checkMain();
    。。。此處有省略代碼。。。
    //建立一個請求對象
    Request request = createRequest(started);
    //簡歷請求的key
    String requestKey = createKey(request);
    。。。。此處有省略代碼。。。

    //創建action,實際上以一個ImageViewAction
    Action action =
        new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
            errorDrawable, requestKey, tag, callback, noFade);
    //提交Action
    picasso.enqueueAndSubmit(action);
  }

上面的代碼為了本篇博文的說明也是去掉了一些枝枝蔓蔓,甚至里面不乏重要的代碼,在本篇不做論述,后面博文會講到。into的主要邏輯就是把request+target封裝交給action,然后由Picasso對此action進行提交。

Action的簡要說明

為什么既然有了Request對象,又會需要一個Action呢?其實仔細分析源碼可以發現Request側重點在于請求本身:比如請求圖片資源的uri,對請求的圖片做什么處理等等一系列請求,主要是對Image做什么樣的展示效果處理等。而Action的所含有的屬性如下:

final Picasso picasso;//持有上文單利引用
  final Request request;//上文提到的request
  final WeakReference target;//target可能是ImageView,或者優先理解為ImageView,為弱引用,確保target被回收的時候不受影響
  final boolean noFade;
  final int memoryPolicy;//緩存策略
  final int networkPolicy;//
  final int errorResId;
  final Drawable errorDrawable;
  final String key;
  final Object tag;
  boolean willReplay;
  boolean cancelled;

很顯然Action的主要職責就是:對圖片進行加載,配置圖片的文件緩存和內存緩存策略以及是否重新加載等邏輯。使得責任分明,調理清晰。
Action是一個抽象的泛型類,提供了complete和error兩個抽象方法、它的子類又如下幾個:
GetAction、FetchAction、ImageViewAction、TargetAction在此處我們提交的是ImageViewAction.
讓我們簡單的看一下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 context = picasso.context;
    boolean indicatorsEnabled = picasso.indicatorsEnabled;
    //設置圖片
    PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
     //加載圖片完成后執行的操作,有客戶端配置自己的callback
    if (callback != null) {
      callback.onSuccess();
    }
  }

可以發現最終是由PicassoDrawable來完成圖片的顯示,所以繼續跟進:
PicasDrawable是BitmapDrawable的子類:

static void setBitmap(ImageView target, Context context, Bitmap bitmap。。) {
    Drawable placeholder = target.getDrawable();//現獲取drawable
    if (placeholder instanceof AnimationDrawable) {
      ((AnimationDrawable) placeholder).stop();
    }
    //生成一個dreawable對象
    PicassoDrawable drawable =
        new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
    target.setImageDrawable(drawable);//最終圖片就這么蹦跶出來了。
  }

上面簡單的介紹了一下Picasso的工作流程,而且故意省略了很多重要的地方因為篇幅需要暫時沒做說明,會繼續寫博客慢慢抽絲撥繭分析,其實閱讀源碼很枯燥,有好多自己能體會到的地方有時候因為語言組織能力有限,只能自己體會而沒辦法寫出來,Picasso的代碼思路也很清晰,建議在業余的時間讀讀加深體會也是好的,好多東西自己鉆研了才會真正的獲得屬于自己的收獲。

相關TAG標簽
上一篇:臺積電:絕大多數7nm客戶都會轉向6nm_IT新聞_博客園
下一篇:最后一頁
相關文章
圖文推薦

關于我們 | 聯系我們 | 廣告服務 | 投資合作 | 版權申明 | 在線幫助 | 網站地圖 | 作品發布 | Vip技術培訓 | 舉報中心

版權所有: 紅黑聯盟--致力于做實用的IT技術學習網站

美女MM131爽爽爽毛片