Picasso原始碼分析(一):單例模式、建造者模式、面向介面程式設計

王世暉發表於2016-06-12

Picasso原始碼分析(一):單例模式、建造者模式、面向介面程式設計
Picasso原始碼分析(二):預設的下載器、快取、執行緒池和轉換器
Picasso原始碼分析(三):快照功能實現和HandlerThread的使用
Picasso原始碼分析(四):不變模式、建造者模式和Request的預處理
Picasso原始碼分析(五):into方法追本溯源和責任鏈模式建立BitmapHunter
Picasso原始碼分析(六):BitmapHunter與請求結果的處理

Picasso簡單介紹

Picasso使用建造者設計模式,使非同步網路載入圖片到控制元件這一複雜流程可以用一條方法鏈搞定。典型的Picasso載入圖片的使用方式如下:

Picasso.with(context).load(url).into(imageView); 

傳入上下文context構造獲取Picasso然後非同步載入圖片連結獲取圖片最後顯示在控制元件上,一氣呵成
或者可以對載入到的圖片進行尺寸壓縮後再顯示到控制元件,如下

Picasso.with(context).load(url).resize(width,height).centerInside().into(imageView);

也可以設定圖片載入過程中和載入失敗後的佔點陣圖

    Picasso .with(context)
            .load(url)
            .placeholder(R.drawable.loading)
            .error(R.drawable.error)
            .into(imageView);

Picasso不僅使用方便,還解決一些圖片載入的硬需求,如使用複雜的圖片壓縮轉換來儘可能的減少記憶體消耗、自帶記憶體和硬碟二級快取功能和對adapter中的控制元件和url進行繫結防止列表滾動造成的圖片錯亂等。

使用單例模式獲取Picasso物件

單例模式提供了對唯一例項的受控訪問,由於在系統記憶體中只存在一個重量級物件(Picasso就是一個重量級物件,佔用較多資源,建立過程比較繁瑣),因此可以節約系統資源,對於一些需要頻繁建立和銷燬的物件單例模式無疑可以提高系統的效能。

  static volatile Picasso singleton = null;
  ..................................................
  public static Picasso with(Context context) {
    if (singleton == null) {
      synchronized (Picasso.class) {
        if (singleton == null) {
          singleton = new Builder(context).build();
        }
      }
    }
    return singleton;
  }

可見Picasso使用標準的雙檢鎖構建執行緒安全的單例,注意靜態屬性singleton使用volatile修飾保證多執行緒對singleton的可見性。

使用建造者模式構建Picasso物件

建造者模式將一個複雜物件的構建與表示分離,使得同樣的構建過程可以建立不同的表示。
按照《Effective Java》的總結: 當構造方法或者靜態工廠方法中的引數過多的時候,尤其是可選引數很多時,考慮使用建造者模式。Picasso的構造器引數就比較多(10個),建造者模式使用的相當精妙。
在with方法單例獲取Picasso中,可以看到Builder的build方法建立並返回了Picasso物件

    singleton = new Builder(context).build();

Builder物件有7屬性需要設定,分別是下載器、執行緒池、快取、監聽器、轉換器、RequestHandler列表及Bitmap的設定

    private Downloader downloader;
    private ExecutorService service;
    private Cache cache;
    private Listener listener;
    private RequestTransformer transformer;
    private List<RequestHandler> requestHandlers;
    private Bitmap.Config defaultBitmapConfig;

Builder的這7個屬性和Picasso這7個屬性是一一對應的,在最終的Builder方法中會將這些屬性傳入Picasso的建構函式進行建立。
對每一個屬性都單獨提供了有返回值的setter方法,返回this引用,這樣setter方法就可以達到鏈式呼叫的目的。以下載器的設定方法為例

    public Builder downloader(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;
    }

首先對空指標和重複設定進行檢查,檢查通過後對屬性進行賦值,最後返回this引用。
通過方法引數對下載器進行了依賴注入,引數的型別是Downloader介面而不是具體的實現,體現了面向介面程式設計的思想,這樣Picasso避免了和某一個具體下載器的耦合關係,並且方便使用者定義自己的下載器,只要定義的下載器實現了Downloader介面並覆寫了相應的方法即可,從而使Picasso非常的靈活和方便擴充套件。其他幾個屬性的setter類似,不再贅述。
這樣通過一些列的setter配置屬性後,最終通過build方法建立例項

    public Picasso build() {
      Context context = this.context;

      if (downloader == null) {
        downloader = Utils.createDefaultDownloader(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);
    }

可見build方法對下載器、快取、執行緒池和轉換器進行了預設設定,之後建立了Stats物件(提供統計和快照功能),Dispatcher物件(任務分發),最後把10個屬性(上下文context和Builder的7個屬性加stats和dispatcher )傳遞給了Picasso的構造器進行構造,最後返回了構造的Picasso物件。

相關文章