老生常談了,但是還是有很多細節(jié)不知道的。先挖個坑,后面慢慢埋~
圖片加載框架?
- Picasso
- Glide
- Fresco
Picasso:
Picasso :和Square的網(wǎng)絡庫一起能發(fā)揮最大作用,因為Picasso可以選擇將網(wǎng)絡請求的緩存部分交給了okhttp實現(xiàn)。
Glide:模仿了Picasso的API,而且在他的基礎上加了很多的擴展(比如gif等支持),Glide默認的Bitmap格式是RGB_565,比 Picasso默認的ARGB_8888格式的內(nèi)存開銷要小一半;Picasso緩存的是全尺寸的(只緩存一種),而Glide緩存的是跟ImageView尺寸相同的(即56*56和128*128是兩個緩存) 。
FB的圖片加載框架Fresco:最大的優(yōu)勢在于5.0以下(最低2.3)的bitmap加載。在5.0以下系統(tǒng),F(xiàn)resco將圖片放到一個特別的內(nèi)存區(qū)域(Ashmem區(qū))。當然,在圖片不顯示的時候,占用的內(nèi)存會自動被釋放。這會使得APP更加流暢,減少因圖片內(nèi)存占用而引發(fā)的OOM。為什么說是5.0以下,因為在5.0以后系統(tǒng)默認就是存儲在Ashmem區(qū)了。
Fresco性能上的優(yōu)點
優(yōu)一:
1、支持webp格式的圖片,是Google官方推行的,它的大小比其它格式圖片的大小要小一半左右,目前各個大公司都漸入的使用這種圖片格式了,比如:Youtube、Gmail、淘寶、QQ空間等都已嘗鮮,使用該格式最大的優(yōu)點就是輕量、省流量、圖片加載迅速。而Fresco是通過jni來實現(xiàn)支持WebP格式圖片。
優(yōu)二:
2、5.0以下系統(tǒng):使用”ashmem”(匿名共享內(nèi)存)區(qū)域存儲Bitmap緩存,這樣Bitmap對象的創(chuàng)建、釋放將永遠不會觸發(fā)GC,關于”ashmem”存儲區(qū)域,它是一個不在Java堆區(qū)的一片存儲內(nèi)存空間,它的管理由Linux內(nèi)核驅動管理,不必深究,只要知道這塊存儲區(qū)域是別于堆內(nèi)存之外的一塊空間就行了,且這塊空間是可以多進程共享的,GC的活動不會影響到它。5.0以上系統(tǒng),由于內(nèi)存管理的優(yōu)化,所以對于5.0以上的系統(tǒng)Fresco將Bitmap緩存直接放到了堆內(nèi)存中。
關于”ashmem”的存儲區(qū)域,我們的應用程序并不能像訪問堆內(nèi)存一樣直接訪問這塊內(nèi)存塊,但是也有一些例外,對于Bitmap而言,有一種為”Purgeable Bitmap”可擦除的Bitmap位圖是存儲在這塊內(nèi)存區(qū)域中的,BitmapFactory.Options中有這么一個屬性 inPurgeable :
BitmapFactory.Options = new BitmapFactory.Options();
options.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeByteArray(jpeg, 0, jpeg.length, options);
所以通過配置inPurgeable = true這個屬性,這樣解碼出來的Bitmap位圖就存儲在”ashmem”區(qū)域中,之后用到”ashmem”中得圖片時,則把這個圖片從這個區(qū)域中取出來,渲染完畢后則放回這個位置。
既然Fresco中Bitmap緩存在5.0以前是放在”ashmem”中,GC并不會回收它們,且也不會被”ashmeme”內(nèi)置的清除機制回收它們,所以這樣雖然使得在堆中不會造成內(nèi)存泄露,而在這塊區(qū)域可能造成內(nèi)存泄露,F(xiàn)resco中采取的辦法則是使用引用計數(shù)的方式,其中有一個SharedReference這個類,這個類中有這么兩個方法:addReference()和deleteReference(),通過這兩個基本方法來對引用進行計數(shù),一旦計數(shù)為零時,則對應的資源將會清除(如:Bitmap.recycle()等),而Fresco為了考慮更容易被我們使用,又提供了一個CloseableReference類,該類可以說是SharedReference類上功能的封裝,CloseableReference同時也實現(xiàn)了Cloneable、Closeable接口,它在調用.clone()方法時同時會調用addReference()來增加一個引用計數(shù),在調用.close()方法時同時會調用deleteReference()來刪除一個引用計數(shù),所以在使用Fresco的使用,我們都是與CloseableReference類打交道,使用CloseableReference必須遵循以下兩條規(guī)則:
1、在賦值CloseableReference給新對象的時候,調用.clone()進行賦值
2、在超出作用域范圍的時候,必須調用.close(),通常會在finally代碼塊中調用
void gee() {
CloseableReference<Val> ref = foo();
try {
haa(ref);
} finally {
ref.close();
}
}
遵循這些規(guī)則可以有效地防止內(nèi)存泄漏。
優(yōu)三:
3、使用了三級緩存:Bitmap緩存+未解碼圖片緩存+硬盤緩存。其中前兩個就是內(nèi)存緩存,Bitmap緩存根據(jù)系統(tǒng)版本不同放在了不同內(nèi)存區(qū)域中,而未解碼圖片的緩存只在堆內(nèi)存中,F(xiàn)resco分了兩步做內(nèi)存緩存,這樣做有什么好處呢?第一個好處就如上的第二條,第二個好處是加快圖片的加載速度,F(xiàn)resco的加載圖片的流程為:查找Bitmap緩存中是否存在,存在則直接返回Bitmap直接使用,不存在則查找未解碼圖片的緩存,如果存在則進行Decode成Bitmap然后直接使用并加入Bitmap緩存中,如果未解碼圖片緩存中查找不到,則進行硬盤緩存的檢查,如有,則進行IO、轉化、解碼等一系列操作,最后成Bitmap供我們直接使用,并把未解碼(Encode)的圖片加入未解碼圖片緩存,把Bitmap加入Bitmap緩存中,如硬盤緩存中沒有,則進行Network操作下載圖片,然后加入到各個緩存中。
既然Fresco使用了三級緩存,而有兩級是內(nèi)存緩存,所以當我們的App在后臺時或者在內(nèi)存低的情況下在onLowMemory()方法中,我們應該手動清除應用的內(nèi)存緩存,我們可以使用下面的方式:
ImagePipeline imagePipeline = Fresco.getImagePipeline();
//清空內(nèi)存緩存(包括Bitmap緩存和未解碼圖片的緩存)
imagePipeline.clearMemoryCaches();
//清空硬盤緩存,一般在設置界面供用戶手動清理
imagePipeline.clearDiskCaches();
//同時清理內(nèi)存緩存和硬盤緩存
imagePipeline.clearCaches();
3.總結:
Picasso所能實現(xiàn)的功能,Glide都能做,無非是所需的設置不同。但是Picasso體積比起Glide小太多如果項目中網(wǎng)絡請求本身用的就是okhttp或者retrofit(本質還是okhttp),那么建議用Picasso,體積會小很多(Square全家桶的干活)。Glide的好處是大型的圖片流,比如gif、Video,如果你們是做美拍、愛拍這種視頻類應用,建議使用。
Fresco在5.0以下的內(nèi)存優(yōu)化非常好,代價就是體積也非常的大,按體積算Fresco>Glide>Picasso
不過在使用起來也有些不便(小建議:他只能用內(nèi)置的一個ImageView來實現(xiàn)這些功能,用起來比較麻煩,我們通常是根據(jù)Fresco自己改改,直接使用他的Bitmap層)
MemoryFile 可以用來測試共享內(nèi)存