本文由hss01248投稿。
hss01248的部落格位址:
http://blog.csdn.net/hss01248
本文是作者在使用Fresco過程中一些經驗分享,包含了大量的經驗,針對一些場景都給出處理的方案,感謝作者分享。
[Fresco](https://github.com/facebook/fresco) 是Facebook開源的安卓上的圖檔加載架構,也可以說是至今為止安卓上最強大的圖檔加載架構。
相對于其他幾個圖檔加載架構,Fresco主要的優點在于更好的記憶體管理和更強大的功能,更便捷的使用,缺點則是體積比較大,引入後會導緻應用apk增加1.5M到2M的大小,但是相對于其便捷性來講,我覺得這都不是事兒.
優點一:記憶體管理
對于5.0以下系統,fresco使用”ashmem”(匿名共享記憶體)區域存儲Bitmap緩存,這樣Bitmap對象的建立、釋放将不會觸發GC,不會占用javaheap.這個特點是其他圖檔加載架構所沒有做到的.
5.0以上系統,由于安卓系統本身記憶體管理的優化,是以對于5.0以上的系統Fresco将Bitmap緩存直接放到了javaheap記憶體中.并且,fresco實作了真正的三級緩存:兩級的記憶體緩存+一個磁盤緩存.兩個記憶體緩存為:bitmap緩存 和未解碼的圖檔緩存,這樣既可以加快圖檔的加載速度,又能節省記憶體的占用.這個兩級的記憶體緩存也是其他圖檔加載架構所沒有做到的.
另外提一點,在app切換到背景時,fresco會自動清理兩級的記憶體緩存,無需手動.
通過以上幾點,使用fresco加載圖檔時記憶體占用要比其他圖檔加載架構小一大半,基本很少發生oom的事情.
幾個圖檔加載架構的記憶體占用測試結果對比請戳這裡:
- Android Image Loader 第三方庫對比測試(http://m.blog.csdn.net/article/details?id=46959649)
優點二:更便捷的使用:
另外再說一句,fresco還支援webp,是以,splash和引導界面的大圖我一般都是用智圖(http://zhitu.isux.us/)壓成webp放在drawable中,用fresco加載就行了.
- [官方文檔中文版](http://fresco-cn.org/)
- [中文的Fresco源碼解讀,分析版本:0.7.0](https://github.com/desmond1121/Fresco-Source-Analysis)
- [fresco裡的photoview :用于檢視大圖并随手勢縮放](https://github.com/ongakuer/PhotoDraweeView)
- [bilibili開源的借助fresco加載圖檔的 spannable text view : Bilibili/drawee-text-view](https://github.com/Bilibili/drawee-text-view)
- fresco的bitmap後處理器封裝,可以直接使用](https://github.com/wasabeef/fresco-processors)
首先,終極的解決方法肯定是,用戶端在圖檔請求中帶上需要的寬和高,伺服器将圖檔縮略到該規格後傳回該小圖.這個做得比較好的是七牛.
注意:不管伺服器能不能傳回縮略圖,所存儲的原圖都不應該太大,有時圖檔太大,甚至都無法下載下傳下來(報504之類的錯誤).
- 那麼,如果伺服器隻能拿到原圖或大圖,fresco怎麼縮略顯示?
fresco中提供了三個功能來[生成縮略圖](http://www.fresco-cn.org/docs/resizing-rotating.htm):
- Scaling :畫布操作,通常是由硬體加速的。圖檔實際大小保持不變,它隻不過在繪制時被放大或縮小.使用時需要配置縮放類型fresco:actualImageScaleType,具體類型名與Imageview的ScaleType幾乎一樣.
- Resizing 是一種軟體執行的管道操作。它傳回一張新的,尺寸不同的圖檔,也就是說直接改變bitmap的大小,可惜是單獨使用時,隻支援jpg,當然,結合Downsampling使用時,可以支援除gif以為的所有常見圖檔,包括webp.
- Downsampling 同樣是軟體實作的管道操作。它不是建立一張新的圖檔,而是在解碼時改變圖檔的大小。 同樣是軟體實作的管道操作。它不是建立一張新的圖檔,而是在解碼時改變圖檔的大小。類似于android中的BitmapFactory在decodefile時的inSampleSize,都是指定一個采樣率,預設是關閉的,如果開啟,那麼需要結合Resizing來使用.
綜上,要縮小記憶體占用,以及減少cpu計算量,減少卡頓,應該是Downsampling結合Resizing來使用.其中Downsampling是在Fresco初始化時開啟,而Resizing則是通過建構ImageRequest時通過制定寬高來實作,是以可以定制每一張或每一類圖檔的寬高. 示例代碼如下:
初始化:
利用SimpleDraweeView加載圖檔的一般姿勢:
注意,我這裡沒有去設定DraweeHierarchy,因為依照fresco的設計思維,DraweeHierarchy屬于view層次的東西,應該在xml中配置.當然如果非要設定,請看這裡(http://fresco-cn.org/docs/using-drawees-code.html).
如何避免顯示圖檔時把人的臉部截掉。
這個就要用到Scaling了.圖檔的縮放拉伸以及裁剪模式.具體看文檔(http://fresco-cn.org/docs/scaling.html#_)
可用的縮放類型:
center
居中,無縮放。
centerCrop
保持寬高比縮小或放大,使得兩邊都大于或等于顯示邊界,且寬或高契合顯示邊界。居中顯示。
focusCrop
同centerCrop, 但居中點不是中點,而是指定的某個點。
centerInside
縮放圖檔使兩邊都在顯示邊界内,居中顯示。和 fitCenter 不同,不會對圖檔進行放大。如果圖尺寸大于顯示邊界,則保持長寬比縮小圖檔。
fitCenter
保持寬高比,縮小或者放大,使得圖檔完全顯示在顯示邊界内,且寬或高契合顯示邊界。居中顯示。
fitStart
同上。但不居中,和顯示邊界左上對齊。
fitEnd
同fitCenter, 但不居中,和顯示邊界右下對齊。
fitXY
不儲存寬高比,填充滿顯示邊界。
none
如要使用tile mode顯示, 需要設定為none
這些縮放類型和Android ImageView 支援的縮放類型幾乎一樣.
圖檔預設是centerCrop,那麼在用一個橫向的SimpleDraweeView來顯示一張豎着拍的人像時,就很可能把人的頭部給截掉了,但對于在listview中展示的SimpleDraweeView來說,我們又無法用focusCrop直接指定其居中點在圖檔上半部分某個地方,因為其他圖檔可能是橫着拍的或很正方形的自拍,這個時候怎麼辦?
就需要根據人臉的檢測來設定focusCrop的那個點了,而android從sdk 1.0開始就已經提供了一個人臉識别的類FaceDetector(http://www.cnblogs.com/mainroadlee/p/android_sdk_face_detection.html),原理是通過找眼睛來識别人臉,可以拿到眼睛的中心點坐标,那麼根據該坐标,結合圖檔本身的寬高,計算出針對每張圖檔的focusCrop 需要設定的點,就能夠解決這個問題了.
//TODO 這個還沒有去寫方法,但有一個開源項目facecropper(https://github.com/lafosca/AndroidFaceCropper/blob/master/FaceCropper-library/src/main/java/cat/lafosca/facecropper/FaceCropper.java)可以參考,他們的做法是将一個大的bitmap截圖成小的bitmap。
對于記憶體的緩存,fresco根據圖檔Uri,以及圖檔的resising參數和processor參數綜合生成緩存key來緩存bitmap,而磁盤檔案緩存則是隻根據Uri生成key,那麼,如果要擷取檔案緩存,隻需要知道uri和通過key取file的api就行了,原先的調用鍊較長,故封裝成單個方法:
需要注意的是檔案名字尾不是普通的圖檔字尾(.jpg之類的),而是.cnt,但都是二進制檔案,可以将檔案直接拷貝到指定路徑重命名成正常圖檔字尾即可.當然如果隻是讀取到記憶體做其他用途,可以直接讀取,無需拷貝更改字尾,不影響使用.
以下是讀取緩存檔案的方法.而拷貝到其他檔案目錄的方法也已封裝好于FrescoUtils中.
由于某種原因無法使用SimpleDraweeView來顯示圖檔(比如說彈幕上顯示頭像),而需要直接操作bitmap,那麼要怎麼拿到url傳回的bitmap?
自己建構圖檔請求,然後類似上面的檔案下載下傳,還是采用DataSubscriber來監聽回調,隻不過傳回的不是void,而是CloseableImage的bitmap,
注意,此bitmap對象的緩存由Fresco管理,是以不要去調用bitmap.recycle()之類的方法.
對此bitmap的一些處理,如果處理後的bitmap對象還是指向該bitmap引用,會影響到其他同樣url,width height,并且同樣Postprocessor的圖檔元件的顯示,比如,将該bitmap高斯模糊了,就會影響到其他的這四個參數相同的SimpleDraweeView的顯示.
那麼,如果要不影響,怎麼辦?很簡單,讓四個參數任一一個不同就行.
-
Fresco中在listview之類的快速滑動時停止加載,滑動停止後恢複加載時調用的API是什麼?
話不多說,直接看代碼.
gif圖檔無法顯示成圓形,怎麼辦?(當然有的情況下,jpg也無法顯示圓形) –加一層和圖檔的parentview 背景色一樣的圓形遮罩即可:
很多app都有清除磁盤緩存的功能,那麼fresco怎麼清除緩存呢?
高斯模糊是app裡設定一些背景效果 常用到的手段.
在fresco中,可以通過postprocessor來實作,也可以自己拿到bitmap後将bitmap模糊化後設定到ImageView或SimpleDraweeView(這個不建議,會消除掉SimpleDraweeView的層級結構,變成單純的ImageView)上.
推薦前一種: 用到别人封裝好的BlurPostprocessor(https://github.com/wasabeef/fresco-processors/blob/master/processors/src/main/java/jp/wasabeef/fresco/processors/BlurPostprocessor.java) :