泰國舉行的谷歌開發者論壇上,谷歌爲我們介紹了一個名叫 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
用於保存整個框架中的配置。
重要方法:
- public static RequestManager with(FragmentActivity activity) {
- RequestManagerRetriever retriever = RequestManagerRetriever.get();
- return retriever.get(activity);
- }
用於創建RequestManager,這裏是Glide通過Activity/Fragment生命週期管理Request原理所在,這個類很關鍵、很關鍵、很關鍵,重要的事情我只說三遍。
主要原理是創建一個自定義Fragment,然後通過自定義Fragment生命週期操作RequestManager,從而達到管理Request。
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所有的請求和帶處理的請求。
重要方法:
- @Override
- //開始暫停的請求
- public void onStart() {
- resumeRequests();
- }
- //停止所有的請求
- @Override
- public void onStop() {
- pauseRequests();
- }
-
- //關閉所以的請求
- @Override
- public void onDestroy() {
- requestTracker.clearRequests();
- }
-
- //創建RequestBuild
- public DrawableTypeRequest<String> load(String string) {
- return (DrawableTypeRequest<String>) fromString().load(string);
- }
-
- 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對象
- void release() {
- if (--acquired == 0) {
- listener.onResourceReleased(key, this);
- }
- }
-
- void acquire() {
- ++acquired;
- }
-
- @Override
- public void recycle() {
- isRecycled = true;
- resource.recycle();
- }
acquire和release兩個方法是對資源引用計數;recycle釋放資源,一般在Lrucache飽和時會觸發。
3.8 Engine(重要)
請求引擎,主要做請求的開始的初始化。
3.8.1 load方法
這個方法很長,將分爲幾步分析
(1)獲取MemoryCache中緩存 首先創建當前Request的緩存key,通過key值從MemoryCache中獲取緩存,判斷緩存是否存在。
- private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
- ....
- EngineResource<?> cached = getEngineResourceFromCache(key);
- if (cached != null) {
- cached.acquire();
- activeResources.put(key, new ResourceWeakReference(key, cached, getReferenceQueue()));
- }
- return cached;
- }
-
- @SuppressWarnings("unchecked")
- private EngineResource<?> getEngineResourceFromCache(Key key) {
- Resource<?> cached = cache.remove(key);
-
- final EngineResource result;
- ...
- return result;
- }
(重點)從緩存中獲取的時候使用的cache.remove(key),然後將值保存在activeResources中,然後將Resource的引用計數加一。
優點:
> 正使用的Resource將會在activeResources中,不會出現在cache中,當MemoryCache中緩存飽和的時候或者系統內存不足的時候,清理Bitmap可以直接調用recycle,不用考慮Bitmap正在使用導致異常,加快系統的回收。
(2)獲取activeResources中緩存
activeResources通過弱引用保存recouse ,也是通過key獲取緩存,
- private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable)
(3)判斷當前的請求任務是否已經存在
- EngineJob current = jobs.get(key);
- if (current != null) {
- current.addCallback(cb);
- return new LoadStatus(cb, current);
- }
如果任務請求已經存在,直接將回調事件傳遞給已經存在的EngineJob,用於請求成功後觸發回調。
(4)執行請求任務
- EngineJob engineJob = engineJobFactory.build(key, isMemoryCacheable);
- DecodeJob<T, Z, R> decodeJob = new DecodeJob<T, Z, R>(key, width, height, fetcher, loadProvider, transformation,
- transcoder, diskCacheProvider, diskCacheStrategy, priority);
- EngineRunnable runnable = new EngineRunnable(engineJob, decodeJob, priority);
- jobs.put(key, engineJob);
- engineJob.addCallback(cb);
- engineJob.start(runnable);
3.9 EngineRunnable
請求執行Runnable,主要功能請求資源、處理資源、緩存資源。
- private Resource<?> decodeFromCache() throws Exception {
- Resource<?> result = null;
- try {
- result = decodeJob.decodeResultFromCache();
- } catch (Exception e) {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Exception decoding result from cache: " + e);
- }
- }
-
- if (result == null) {
- result = decodeJob.decodeSourceFromCache();
- }
- return result;
- }
-
- private Resource<?> decodeFromSource() throws Exception {
- return decodeJob.decodeFromSource();
- }
加載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
- 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加速內存回收。