泰國舉行的谷歌開發者論壇上,谷歌為我們介紹了一個名叫 Glide 的圖片載入庫,作者是bumptech。這個庫被廣泛的運用在google的開源專案中,包括2014年google I/O大會上釋出的官方app。
相信現在的App上面或多或少都會涉及到圖片載入,從最初自己編寫http請求下載,到各種第三方的庫的使用。可謂是八仙過海各顯神通,看到有很多博友對現有的庫進行了對比,其中Picasso與Glide對比參照了一下,根據現有專案需求便選擇了Glide。
Glide的特點
- 區別於其它的第三方載入庫,它可以與activity、fragment的生命週期繫結,在Paused暫停載入,在Resumed的時候又自動重新載入。
- 支援Memory和Disk圖片快取
- 支援Gif和Webp格式圖片
- 使用Bitmap Pool可以使Bitmap複用
- 對於回收的Bitmap會自動呼叫recycle,減少系統回收壓力
總體設計
基本概念
- 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。
這裡會將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
成員變數:
- Lifecycle lifecycle,用於監聽RequestManagerFragment生命週期。
- 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