路邊跌倒的老太太我都不服,圖片載入我只服Glide

MissMyDearBear發表於2017-12-22

泰國舉行的谷歌開發者論壇上,谷歌為我們介紹了一個名叫 Glide 的圖片載入庫,作者是bumptech。這個庫被廣泛的運用在google的開源專案中,包括2014年google I/O大會上釋出的官方app。

相信現在的App上面或多或少都會涉及到圖片載入,從最初自己編寫http請求下載,到各種第三方的庫的使用。可謂是八仙過海各顯神通,看到有很多博友對現有的庫進行了對比,其中Picasso與Glide對比參照了一下,根據現有專案需求便選擇了Glide。

Glide的特點

  1. 區別於其它的第三方載入庫,它可以與activity、fragment的生命週期繫結,在Paused暫停載入,在Resumed的時候又自動重新載入。
  2. 支援Memory和Disk圖片快取
  3. 支援Gif和Webp格式圖片
  4. 使用Bitmap Pool可以使Bitmap複用
  5. 對於回收的Bitmap會自動呼叫recycle,減少系統回收壓力

總體設計

路邊跌倒的老太太我都不服,圖片載入我只服Glide

基本概念

  • RequestManager:請求管理,每一個Activity都會建立一個RequestManager,根據對應Activity的生命週期管理該Activity上所有的圖片請求
  • Engine:載入圖片的引擎,根據Request建立EngineJob和DecodeJob
  • EngineJob:圖片載入
  • DecodeJob:圖片處理

核心類介紹

3.1 glide

用於儲存整個框架中的配置。 重要方法:

 public static RequestManager with(Context context) {
        RequestManagerRetriever retriever = RequestManagerRetriever.get();
        return retriever.get(context);
    }
複製程式碼

用於建立RequsetManager,這裡是Glide通過Activity/Fragment生命週期管理Request的原理所在,整個類很關鍵,主要原理是建立一個自定義的Fragment,然後通過自定義Fragment生命週期操作RequestManager,從而達到管理request。

路邊跌倒的老太太我都不服,圖片載入我只服Glide
這裡會將RequestManagerFragment生命週期事件回撥通過RequestManager的建構函式傳值。所以RequestManage就能響應RequestManagerFragment的生命週期

3.2 RequestManagerRetriever

 RequestManager supportFragmentGet(Context context, FragmentManager fm) {
        SupportRequestManagerFragment current = getSupportRequestManagerFragment(fm);
        RequestManager requestManager = current.getRequestManager();
        if (requestManager == null) {
            requestManager = new RequestManager(context, current.getLifecycle(), current.getRequestManagerTreeNode());
            current.setRequestManager(requestManager);
        }
        return requestManager;
    }
複製程式碼

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

3.3 RequestManager

成員變數:

  1. Lifecycle lifecycle,用於監聽RequestManagerFragment生命週期。
  2. RequestTracker requestTracker, 用於儲存當前RequestManager所有的請求和帶處理的請求。 重要方法:
/**
     * Lifecycle callback that registers for connectivity events (if the android.permission.ACCESS_NETWORK_STATE
     * permission is present) and restarts failed or paused requests.
     */
    @Override
    public void onStart() {
        // onStart might not be called because this object may be created after the fragment/activity's onStart method.
        resumeRequests();
    }

    /**
     * Lifecycle callback that unregisters for connectivity events (if the android.permission.ACCESS_NETWORK_STATE
     * permission is present) and pauses in progress loads.
     */
    @Override
    public void onStop() {
        pauseRequests();
    }

    /**
     * Lifecycle callback that cancels all in progress requests and clears and recycles resources for all completed
     * requests.
     */
    @Override
    public void onDestroy() {
        requestTracker.clearRequests();
    }
    ....
        /**
     * Returns a request builder that uses the {@link com.bumptech.glide.load.model.ModelLoaderFactory}s currently
     * registered for the given model class for {@link InputStream}s and {@link ParcelFileDescriptor}s to load a
     * thumbnail from either the image or the video represented by the given model.
     *
     * <p>
     *     Note - for maximum efficiency, consider using {@link #from(Class)}} to avoid repeatedly allocating builder
     *     objects.
     * </p>
     *
     * @see #from(Class)
     *
     * @param model The model the load.
     * @param <T> The type of the model to load.
     */
    public <T> DrawableTypeRequest<T> load(T model) {
        return (DrawableTypeRequest<T>) loadGeneric(getSafeClass(model)).load(model);
    }
    .........
    public <Y extends Target<TranscodeType>> Y into(Y target) {
  	...
  	Request previous = target.getRequest();
  	//停止當前target中的Request。
 	if (previous != null) {
 		previous.clear(); //這個地方很關鍵,見Request解析
  		requestTracker.removeRequest(previous);
  		previous.recycle();
   	}
  	...
   	return target;
   }
 
複製程式碼

3.4 DrawableRequestBuilder

用於建立Request。 這裡麵包括很多方法,主要是配置載入圖片的url、大小、動畫、ImageView物件、自定義圖片處理介面等。

3.5 Request

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

   @Override
 public void clear() {
  	...
  	if (resource != null) {
   		//這裡會釋放資源
 		releaseResource(resource);
  	}
  	...
 }

複製程式碼

這裡的基本原理是當有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方法

這個方法很長,將分為幾步分析

獲取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正在使用導致異常,加快系統的回收。


獲取activeResources中快取

activeResources通過弱引用儲存recouse ,也是通過key獲取快取,

  1. private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable)
複製程式碼

判斷當前的請求任務是否已經存在
  1. EngineJob current = jobs.get(key);
  2. if (current != null) {
  3. 	current.addCallback(cb);
  4. 	return new LoadStatus(cb, current);
  5. }
複製程式碼

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


執行請求任務
  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

 public Resource<Z> decodeResultFromCache() throws Exception  
 public Resource<Z> decodeSourceFromCache() throws Exception
從快取中獲取處理後的資源。上面有關Key的內容,Key是一個物件,可以獲取key和orginKey。decodeResultFromCache就是通過key獲取快取,decodeSourceFromCache()就是通過orginKey獲取快取。
 private Resource<Z> transformEncodeAndTranscode(Resource<T> decoded)
處理和包裝資源;快取資源。
 //儲存原資源
 private Resource<T> cacheAndDecodeSourceData(A data) throws IOException  
 //儲存處理後的資源
 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時候的重複利用。

Glide使用

由於篇幅過長,請點我檢視具體使用

其它

本文參考地址:http://m.blog.csdn.net/article/details?id=49514465

相關文章