分享glide使用教程「glide原理」

一、前言

Glide 是安卓平台上媒體管理和圖片加載框架,它內部封裝了媒體解碼工具、內存和磁盤緩存以及資源池等,並向用戶暴露簡單易用的接口。我們可以用它來獲取、解碼、並展示視頻、圖片和 GIF 動畫。如果大家有用過 Picasso 應該知道,Glide 的使用方式和 Picasso 非常相似,甚至很多 API 的名稱都一樣。Glide是一個優秀的圖片加載庫,它有如下優點:

1. Glide可以監聽Activity的生命周期管理,更加合理的管理圖片的加載和釋放。

2. 加載質量,Picasso默認採用的ARGB-8888, Glide默認採用的是RGB-565,內存佔用會減小一半。

3. Glide可以加載Gif圖。

4. 緩存策略和加載速度。Picasso緩存的是全尺寸,而Glide的緩存的圖片和ImageView的尺寸相同。Glide的這個特點,讓加載顯得特別的快,而Picasso則因為需要在顯示之前重新調整大小而導致一些延遲。

5. Glide可以通過自定義GlideMoudle來完成特殊的加載需求,例如加載加密的圖片等。接下來我們就從源碼的角度來探究一下 Glide 的內部原理。

二、源碼

Glide.with(……)

我們來看一下Gilde的源碼

/**

* Begin a load with Glide by passing in a context.

*

* <p>

* Any requests started using a context will only have the application level options applied and will not be

* started or stopped based on lifecycle events. In general, loads should be started at the level the result

* will be used in. If the resource will be used in a view in a child fragment,

* the load should be started with {@link #with(android.app.Fragment)}} using that child fragment. Similarly,

* if the resource will be used in a view in the parent fragment, the load should be started with

* {@link #with(android.app.Fragment)} using the parent fragment. In the same vein, if the resource will be used

* in a view in an activity, the load should be started with {@link #with(android.app.Activity)}}.

* </p>

*

* <p>

* This method is appropriate for resources that will be used outside of the normal fragment or activity

* lifecycle (For example in services, or for notification thumbnails).

* </p>

*

* @see #with(android.app.Activity)

* @see #with(android.app.Fragment)

* @see #with(android.support.v4.app.Fragment)

* @see #with(android.support.v4.app.FragmentActivity)

*

* @param context Any context, will not be retained.

* @return A RequestManager for the top level application that can be used to start a load.

*/

public static RequestManager with(Context context) {

RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(context);

}

/**

* Begin a load with Glide that will be tied to the given {@link android.app.Activity}’s lifecycle and that uses the

* given {@link Activity}’s default options.

*

* @param activity The activity to use.

* @return A RequestManager for the given activity that can be used to start a load.

*/

public static RequestManager with(Activity activity) {

RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(activity);

}

/**

* Begin a load with Glide that will tied to the give {@link android.support.v4.app.FragmentActivity}’s lifecycle

* and that uses the given {@link android.support.v4.app.FragmentActivity}’s default options.

*

* @param activity The activity to use.

* @return A RequestManager for the given FragmentActivity that can be used to start a load.

*/

public static RequestManager with(FragmentActivity activity) {

RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(activity);

}

/**

* Begin a load with Glide that will be tied to the given {@link android.app.Fragment}’s lifecycle and that uses

* the given {@link android.app.Fragment}’s default options.

*

* @param fragment The fragment to use.

* @return A RequestManager for the given Fragment that can be used to start a load.

*/

@TargetApi(Build.VERSION_CODES.HONEYCOMB)

public static RequestManager with(android.app.Fragment fragment) {

RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(fragment);

}

/**

* Begin a load with Glide that will be tied to the given {@link android.support.v4.app.Fragment}’s lifecycle and

* that uses the given {@link android.support.v4.app.Fragment}’s default options.

*

* @param fragment The fragment to use.

* @return A RequestManager for the given Fragment that can be used to start a load.

*/

public static RequestManager with(Fragment fragment) {

RequestManagerRetriever retriever = RequestManagerRetriever.get(); return retriever.get(fragment);

}

總結:可以傳入的參數有

*@see #with(android.app.Activity)

* @see #with(android.app.Fragment)

* @see #with(android.support.v4.app.Fragment)

* @see #with(android.support.v4.app.FragmentActivity)

同時將Activity/Fragment作為with()參數的好處是:圖片加載會和Activity/Fragment的生命周期保持一致,比如Paused狀態在暫停加載,在Resumed的時候又自動重新加載。所以我建議傳參的時候傳遞Activity 和 Fragment給Glide,而不是Context。

三、原理

接下來我想說Glide的原理,下圖是glide原理簡圖。Glide 源碼原理簡述

glide原理簡圖

1.Glide的資源獲取組件:

· Model: 原始資源,比如Url,AndroidResourceId, File等

· Data: 中間資源,比如Stream,ParcelFileDescriptor(ContentProvider共享文件時比較常用,其實就是操作系統的文件描述符的封裝,裏面有in out err三個取值。也有人說是鏈接建立好之後的socket句柄。)等

· Resource:直接使用的資源,包括Bitmap,Drawable等

2.Glide庫的資源復用:

· Android的內存申請幾乎都在new的時候發生,而new較大對象(比如Bitmap時),更加容易觸發GC_FOR_ALLOW。所以Glide盡量的復用資源來防止不必要的GC_FOR_ALLOC引起卡頓。

· 最顯著的內存復用就是內存LruResourceCache(第一次從網絡或者磁盤上讀取到Resource時,並不會保存到LruCache當中,當Resource被release時,也就是View不在需要此Resource時,才會進入LruCache當中)

· 還有BitmapPool(Glide會盡量用圖片池來獲取到可以復用的圖片,獲取不到才會new,而當LruCache觸發Evicted時會把從LruCache中淘汰下來的Bitmap回收,也會把transform時用到的中間Bitmap加以復用及回收)

3.Glide庫圖片池:

· 以前是Bitmap復用必須長寬相等才可以復用

· 及以後是Size>=所需就可以復用,只不過需要調用reconfigure來調整尺寸

· Glide用AttributeStategy和SizeStrategy來實現兩種策略

· 圖片池在收到傳來的Bitmap之後,通過長寬或者Size來從KeyPool中獲取Key(對象復用到了極致,連Key都用到了Pool),然後再每個Key對應一個雙向鏈表結構來存儲。每個Key下可能有很多個待用Bitmap

· 取出後要減少圖片池中記錄的當前Size等,並對Bitmap進行eraseColor(Color.TRANSPAENT)操作確保可用

4.Glide加載發起流程:

1. Glide.with(context)創建RequestManager

· RequestManager負責管理當前context的所有Request

· Context可以傳Fragment、Activity或者其他Context,當傳Fragment、Activity時,當前頁面對應的Activity的生命周期可以被RequestManager監控到,從而可以控制Request的pause、resume、clear。這其中採用的監控方法就是在當前activity中添加一個沒有view的fragment,這樣在activity發生onStart onStop onDestroy的時候,會觸發此fragment的onStart onStop onDestroy。

· RequestManager用來跟蹤眾多當前頁面的Request的是RequestTracker類,用弱引用來保存運行中的Request,用強引用來保存暫停需要恢復的Request。

2. Glide.with(context).load(url)創建需要的Request

· 通常是DrawableTypeRequest,後面可以添加transform、fitCenter、animate、placeholder、error、override、skipMemoryCache、signature等等

· 如果需要進行Resource的轉化比如轉化為Byte數組等需要,可以加asBitmap來更改為BitmapTypeRequest

· Request是Glide加載圖片的執行單位

3. Glide.with(context).load(url).into(imageview)

· 在Request的into方法中會調用Request的begin方法開始執行

· 在正式生成EngineJob放入Engine中執行之前,如果並沒有事先調用override(width, height)來指定所需要寬高,Glide則會嘗試去獲取imageview的寬和高,如果當前imageview並沒有初始化完畢取不到高寬,Glide會通過view的ViewTreeObserver來等View初始化完畢之後再獲取寬高再進行下一步

5.Glide加載資源:

· GlideBuilder在初始化Glide時,會生成一個執行機Engine

· Engine中包含LruCache緩存及一個當前正在使用的active資源Cache(弱引用)

· activeCache輔助LruCache,當Resource從LruCache中取出使用時,會從LruCache中remove並進入acticeCache當中

· Cache優先級LruCache>activeCache

· Engine在初始化時要傳入兩個ExecutorService,即會有兩個線程池,一個用來從DiskCache獲取resource,另一個用來從Source中獲取(通常是下載)

· 線程的封裝單位是EngineJob,有兩個順序狀態,先是CacheState,在此狀態先進入DiskCacheService中執行獲取,如果沒找到則進入SourceState,進到SourceService中執行下載

6.Glide的Target:

負責圖片加載的回調。

四、總結

以上便是 Glide 的源碼和原理,它內部的運作實在是太複雜了,但正是這種複雜的內部實現使得 Glide 的可擴展型得到增強。比如我們可以自定義磁盤緩存策略,自定義 ModelLoader 以實現 Model 到 Data 的轉換,也可以自定義 ResourceDecoder 和 ResourceTranscoder 來實現資源的解碼和轉碼。用一張圖來總結整個加載過程:

Glide 源碼原理簡述

好啦,文章寫到這裡就結束了,如果你覺得文章寫得不錯就給個讚唄!如果你覺得那裡值得改進的,請給我留言。一定會認真查閱,修正不足。謝謝!更多更詳細更專業android架構資料。

原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/255780.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
投稿專員的頭像投稿專員
上一篇 2024-12-15 12:30
下一篇 2024-12-15 12:31

相關推薦

發表回復

登錄後才能評論