以前項目上用的是Volley的ImageViewRequest進行圖片加載的。可后來隨着的項目的精細化,我覺得將圖片請求和json的數據請求分離使用這樣會更好。在網上查了很多資料,也進行了實踐比較。我選擇了okHhttp+Glide作為網絡請求的方式在項目上。
okHttp我會在以后文章介紹,今天只要看看Glide用法與原理分析。這些都是我在網上整理的,大家如果要看原創,這是網址Google推薦的圖片加載庫Glide介紹
Glide是谷歌為我們介紹了一個圖片加載庫,作者是bumptech。這個庫被廣泛的運用在google的開源項目中,包括2014年google I/O大會上發布的官方app。 它的使用比較方便。需要V4包的支持。導入Glide架包即可。
Glide.with(context)
.load("http://inthecheesefactory.com/uploads/source/glidepicasso/cover.jpg")
.into(ImageView);
這里的Context可以是Activity,Fragment等。它默認的Bitmap的格式RGB_565,同時他還可以指定圖片大小;默認使用HttpUrlConnection下載圖片,可以配置為OkHttp或者Volley下載,也可以自定義下載方式。
Glide支持圖片磁盤緩存,默認是內部存儲。Glide緩存的是跟ImageView尺寸相同的。
Glide.with(this)
.load("http://nuuneoi.com/uploads/source/playstore/cover.jpg")
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(ivImgGlide);
這樣不僅可以緩存ImageView大小尺寸還可以緩存其他尺寸。下次再加載ImageView的圖片時,全尺寸的圖片將會從緩存中取出,重新調整大小,然后再次緩存。這樣加載圖片顯示會很快。
下來我想說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庫圖片池:
- 4.4以前是Bitmap復用必須長寬相等才可以復用
- 4.4及以后是Size>=所需就可以復用,只不過需要調用reconfigure來調整尺寸
- Glide用AttributeStategy和SizeStrategy來實現兩種策略
- 圖片池在收到傳來的Bitmap之后,通過長寬或者Size來從KeyPool中獲取Key(對象復用到了極致,連Key都用到了Pool),然后再每個Key對應一個雙向鏈表結構來存儲。每個Key下可能有很多個待用Bitmap
- 取出后要減少圖片池中記錄的當前Size等,並對Bitmap進行eraseColor(Color.TRANSPAENT)操作確保可用
4.Glide加載發起流程:
- 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。
- Glide.with(context).load(url)創建需要的Request
- 通常是DrawableTypeRequest,后面可以添加transform、fitCenter、animate、placeholder、error、override、skipMemoryCache、signature等等
- 如果需要進行Resource的轉化比如轉化為Byte數組等需要,可以加asBitmap來更改為BitmapTypeRequest
- Request是Glide加載圖片的執行單位
- 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庫在使用過程中表現較好,滑動流暢,內存占用低
- 代碼擴展性極強,4.0版本更加如此,但來的問題就是過於復雜,不太便於閱讀
- 但仍會遇到有些需求Glide無法滿足
- 設置加載圖片的最大寬高
- PlaceHolder的圖片形狀不與加載的Bitmap相同會產生的抖動問題
- 無法指定刪除某一個圖片緩存的問題(可以用加signature的方式試其失效並重新下載,但不可以刪除)
在網上我還看到有Picasso的圖片加載框架。其實和Glide用法很相似。