Glide 源碼解析

泰國舉行的谷歌開發者論壇上,谷歌爲我們介紹了一個名叫 Glide 的圖片加載庫,作者是bumptech。這個庫被廣泛的運用在google的開源項目中,包括2014年google I/O大會上發佈的官方app。Glide和Picasso使用上有90%的相似度,但是內部實現機制有很大區別 Glide介紹

1.主要特點

(1)支持Memory和Disk圖片緩存。
(2)支持gif和webp格式圖片。
(3)根據Activity/Fragment生命週期自動管理請求。
(4)使用Bitmap Pool可以使Bitmap複用。
(5)對於回收的Bitmap會主動調用recycle,減小系統回收壓力。

2. 總體設計

總體設計圖

基本概念

RequestManager:請求管理,每一個Activity都會創建一個RequestManager,根據對應Activity的生命週期管理該Activity上所以的圖片請求。

Engine:加載圖片的引擎,根據Request創建EngineJob和DecodeJob。

EngineJob:圖片加載。

DecodeJob:圖片處理。

流程圖

這裏是大概的總體流程圖, 具體的細節中流程下面繼續分析。
總體流程圖

3. 核心類介紹

3.1 Gilde 
用於保存整個框架中的配置。

重要方法:

  1. public static RequestManager with(FragmentActivity activity) {
  2. RequestManagerRetriever retriever = RequestManagerRetriever.get();
  3. return retriever.get(activity);
  4. }

用於創建RequestManager,這裏是Glide通過Activity/Fragment生命週期管理Request原理所在,這個類很關鍵、很關鍵、很關鍵,重要的事情我只說三遍。
主要原理是創建一個自定義Fragment,然後通過自定義Fragment生命週期操作RequestManager,從而達到管理Request。

RequestManager創建流程圖

3.2 RequestManagerRetriever

  1. RequestManager supportFragmentGet(Context context, FragmentManager fm) {
  2. SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);
  3. RequestManager requestManager = current.getRequestManager();
  4. if (requestManager == null) {
  5. requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
  6. current.setRequestManager(requestManager);
  7. }
  8. return requestManager;
  9. }

這裏判斷是否只當前RequestManagerFragment是否存在RequestManager,保證一個Activity對應一個RequestManager, 這樣有利於管理一個Activity上所有的Request。創建RequestManager的時候會將RequestManagerFragment中的回調接口賦值給RequestManager,達到RequestManager監聽RequestManagerFragment的生命週期。

3.3 RequestManager
成員變量:
(1)Lifecycle lifecycle,用於監聽RequestManagerFragment生命週期。
(2)RequestTracker requestTracker, 用於保存當前RequestManager所有的請求和帶處理的請求。

重要方法:

  1. @Override
  2. //開始暫停的請求
  3. public void onStart() {
  4. resumeRequests();
  5. }
  6. //停止所有的請求
  7. @Override
  8. public void onStop() {
  9. pauseRequests();
  10. }
  11.  
  12. //關閉所以的請求
  13. @Override
  14. public void onDestroy() {
  15. requestTracker.clearRequests();
  16. }
  17.  
  18. //創建RequestBuild
  19. public DrawableTypeRequest<String> load(String string) {
  20. return (DrawableTypeRequest<String>) fromString().load(string);
  21. }
  22.  
  23. public <Y extends Target<TranscodeType>> Y into(Y target) {
  24. ...
  25. Request previous = target.getRequest();
  26. //停止當前target中的Request。
  27. if (previous != null) {
  28. previous.clear(); //這個地方很關鍵,見Request解析
  29. requestTracker.removeRequest(previous);
  30. previous.recycle();
  31. }
  32. ...
  33. return target;
  34. }

3.4 DrawableRequestBuilder 
用於創建Request。 這裏麪包括很多方法,主要是配置加載圖片的url、大小、動畫、ImageView對象、自定義圖片處理接口等。

3.5 Request 
主要是操作請求,方法都很簡單。

  1. @Override
  2. public void clear() {
  3. ...
  4. if (resource != null) {
  5. //這裏會釋放資源
  6. releaseResource(resource);
  7. }
  8. ...
  9. }

這裏的基本原理是當有Target使用Resource(Resource見下文)時,Resource中的引用記數值會加一,當釋放資源Resource中的引用記數值減一。當沒有Target使用的時候就會釋放資源,放進Lrucache中。

3.7 EngineResource
實現Resource接口,使用裝飾模式,裏面包含實際的Resource對象

  1. void release() {
  2. if (--acquired == 0) {
  3. listener.onResourceReleased(key, this);
  4. }
  5. }
  6.  
  7. void acquire() {
  8. ++acquired;
  9. }
  10.  
  11. @Override
  12. public void recycle() {
  13. isRecycled = true;
  14. resource.recycle();
  15. }

acquire和release兩個方法是對資源引用計數;recycle釋放資源,一般在Lrucache飽和時會觸發。

3.8 Engine(重要) 
請求引擎,主要做請求的開始的初始化。

3.8.1 load方法
這個方法很長,將分爲幾步分析

(1)獲取MemoryCache中緩存 首先創建當前Request的緩存key,通過key值從MemoryCache中獲取緩存,判斷緩存是否存在。

  1. private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
  2. ....
  3. EngineResource<?> cached = getEngineResourceFromCache(key);
  4. if (cached != null) {
  5. cached.acquire();
  6. activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
  7. }
  8. return cached;
  9. }
  10.  
  11. @SuppressWarnings("unchecked")
  12. private EngineResource<?> getEngineResourceFromCache(Key key) {
  13. Resource<?> cached = cache.remove(key);
  14.  
  15. final EngineResource result;
  16. ...
  17. return result;
  18. }

(重點)從緩存中獲取的時候使用的cache.remove(key),然後將值保存在activeResources中,然後將Resource的引用計數加一。
優點:
> 正使用的Resource將會在activeResources中,不會出現在cache中,當MemoryCache中緩存飽和的時候或者系統內存不足的時候,清理Bitmap可以直接調用recycle,不用考慮Bitmap正在使用導致異常,加快系統的回收。


(2)獲取activeResources中緩存
activeResources通過弱引用保存recouse ,也是通過key獲取緩存,

  1. private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable)

(3)判斷當前的請求任務是否已經存在

  1. EngineJob current = jobs.get(key);
  2. if (current != null) {
  3. current.addCallback(cb);
  4. return new LoadStatus(cb, current);
  5. }

如果任務請求已經存在,直接將回調事件傳遞給已經存在的EngineJob,用於請求成功後觸發回調。


(4)執行請求任務

  1. EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
  2. DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
  3. transcoder, diskCacheProvider, diskCacheStrategy, priority);
  4. EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
  5. jobs.put(key, engineJob);
  6. engineJob.addCallback(cb);
  7. engineJob.start(runnable);

3.9 EngineRunnable
請求執行Runnable,主要功能請求資源、處理資源、緩存資源。

  1. private Resource<?> decodeFromCache() throws Exception {
  2. Resource<?> result = null;
  3. try {
  4. result = decodeJob.decodeResultFromCache();
  5. } catch (Exception e) {
  6. if (Log.isLoggable(TAG, Log.DEBUG)) {
  7. Log.d(TAG, "Exception decoding result from cache: " + e);
  8. }
  9. }
  10.  
  11. if (result == null) {
  12. result = decodeJob.decodeSourceFromCache();
  13. }
  14. return result;
  15. }
  16.  
  17. private Resource<?> decodeFromSource() throws Exception {
  18. return decodeJob.decodeFromSource();
  19. }

加載DiskCache和網絡資源。加載DiskCache包括兩個,因爲Glide默認是保存處理後的資源(壓縮和裁剪後),緩存方式可以自定義配置。如果客戶端規範設計,ImageView大小大部分相同可以節省圖片加載時間和Disk資源。

3.10 DecodeJob

  1. public Resource<Z> decodeResultFromCache() throws Exception
  2. public Resource<Z> decodeSourceFromCache() throws Exception

從緩存中獲取處理後的資源。上面有關Key的內容,Key是一個對象,可以獲取key和orginKey。decodeResultFromCache就是通過key獲取緩存,decodeSourceFromCache()就是通過orginKey獲取緩存。

  1. private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded)

處理和包裝資源;緩存資源。

  1. //保存原資源
  2. private Resource<T> cacheAndDecodeSourceData(A data) throws IOException
  3. //保存處理後的資源
  4. private void writeTransformedToCache(Resource<T> transformed)

3.11 Transformation

  1. Resource<T> transform(Resource<T> resource, int outWidth, int outHeight);

處理資源,這裏面出現BitmapPool類,達到Bitmap複用。

3.12 ResourceDecoder 
用於將文件、IO流轉化爲Resource

3.13BitmapPool 
用於存放從LruCache中remove的Bitmap, 用於後面創建Bitmap時候的重複利用。

3.雜談

Glide的架構擴展性高,但是難以理解,各種接口、泛型,需要一定的學習才能熟練運用。

Glide的優點:

(1)支持對處理後的資源Disk緩存。
(2)通過BitmapPool對Bitmap複用。
(3)使用activityResources緩存正在使用的resource,對於BitmapPool飽和移除的Bitmap直接調用recycle加速內存回收。

發佈了44 篇原創文章 · 獲贊 7 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章