【Android 庫(kù) 圖片庫(kù)比較】
四大圖片庫(kù)比較
四大圖片緩存基本信息
Universal ImageLoader 是很早開源的圖片緩存淌山,在早期被很多應(yīng)用使用帚屉。
Picasso 是 Square 開源的項(xiàng)目笆豁,且他的主導(dǎo)者是 JakeWharton票髓,所以廣為人知珍策。
Glide 是 Google 員工的開源項(xiàng)目毅人。
Fresco 是 Facebook 的開源的圖片緩存
基本概念
在正式對(duì)比前吭狡,先了解幾個(gè)圖片緩存通用的概念:
- RequestManager:請(qǐng)求生成和管理模塊;
- Engine:引擎部分丈莺,負(fù)責(zé)創(chuàng)建任務(wù)(獲取數(shù)據(jù))划煮,并調(diào)度執(zhí)行;
- GetDataInterface:數(shù)據(jù)獲取接口缔俄,負(fù)責(zé)從各個(gè)數(shù)據(jù)源獲取數(shù)據(jù)般此。比如 MemoryCache 從內(nèi)存緩存獲取數(shù)據(jù)、DiskCache 從本地緩存獲取數(shù)據(jù)牵现,下載器從網(wǎng)絡(luò)獲取數(shù)據(jù)等铐懊。
- Displayer:資源(圖片)顯示器,用于顯示或操作資源瞎疼。 比如 ImageView科乎,這幾個(gè)圖片緩存都不僅僅支持 ImageView,同時(shí)支持其他 View 以及虛擬的 Displayer 概念贼急。
- Processor 資源(圖片)處理器茅茂, 負(fù)責(zé)處理資源,比如旋轉(zhuǎn)太抓、壓縮空闲、截取等。
以上概念的稱呼在不同圖片緩存中可能不同走敌,比如 Displayer 在 ImageLoader 中叫做 ImageAware碴倾,在 Picasso 和 Glide 中叫做 Target。
共同優(yōu)點(diǎn)
1. 使用簡(jiǎn)單
都可以通過一句代碼可實(shí)現(xiàn)圖片獲取和顯示掉丽。
2. 可配置度高跌榔,自適應(yīng)程度高
圖片緩存的下載器(重試機(jī)制)、解碼器捶障、顯示器僧须、處理器、內(nèi)存緩存项炼、本地緩存担平、線程池示绊、緩存算法等大都可輕松配置。
自適應(yīng)程度高暂论,根據(jù)系統(tǒng)性能初始化緩存配置面褐、系統(tǒng)信息變更后動(dòng)態(tài)調(diào)整策略。
比如根據(jù) CPU 核數(shù)確定最大并發(fā)數(shù)空另,根據(jù)可用內(nèi)存確定內(nèi)存緩存大小盆耽,網(wǎng)絡(luò)狀態(tài)變化時(shí)調(diào)整最大并發(fā)數(shù)等蹋砚。
3. 多級(jí)緩存
都至少有兩級(jí)緩存扼菠、提高圖片加載速度。
4. 支持多種數(shù)據(jù)源
支持多種數(shù)據(jù)源坝咐,網(wǎng)絡(luò)循榆、本地、資源墨坚、Assets 等
5. 支持多種 Displayer
不僅僅支持 ImageView秧饮,同時(shí)支持其他 View 以及虛擬的 Displayer 概念。
其他小的共同點(diǎn)包括支持動(dòng)畫泽篮、支持 transform 處理盗尸、獲取 EXIF 信息等。
ImageLoader
總體設(shè)計(jì)及流程
上面是 ImageLoader 的總體設(shè)計(jì)圖帽撑。整個(gè)庫(kù)分為 ImageLoaderEngine泼各,Cache 及 ImageDownloader,ImageDecoder亏拉,BitmapDisplayer扣蜻,BitmapProcessor 五大模塊,其中 Cache 分為 MemoryCache 和 DiskCache 兩部分及塘。
簡(jiǎn)單的講就是 ImageLoader 收到加載及顯示圖片的任務(wù)莽使,并將它交給 ImageLoaderEngine,ImageLoaderEngine 分發(fā)任務(wù)到具體線程池去執(zhí)行笙僚,任務(wù)通過 Cache 及 ImageDownloader 獲取圖片芳肌,中間可能經(jīng)過 BitmapProcessor 和 ImageDecoder 處理,最終轉(zhuǎn)換為Bitmap 交給 BitmapDisplayer 在 ImageAware 中顯示肋层。
ImageLoader 優(yōu)點(diǎn)
(1) 支持下載進(jìn)度監(jiān)聽
(2) 可以在 View 滾動(dòng)中暫停圖片加載
通過 PauseOnScrollListener 接口可以在 View 滾動(dòng)中暫停圖片加載庇勃。
(3) 默認(rèn)實(shí)現(xiàn)多種內(nèi)存緩存算法這幾個(gè)圖片緩存都可以配置緩存算法,不過 ImageLoader 默認(rèn)實(shí)現(xiàn)了較多緩存算法槽驶,如 Size 最大先刪除责嚷、使用最少先刪除、最近最少使用掂铐、先進(jìn)先刪除罕拂、時(shí)間最長(zhǎng)先刪除等揍异。
(4) 支持本地緩存文件名規(guī)則定義
缺點(diǎn)
缺點(diǎn)在于不支持GIF圖片加載, 緩存機(jī)制沒有和http的緩存很好的結(jié)合, 完全是自己的一套緩存機(jī)制
Picasso
總體設(shè)計(jì)及流程
上面是 Picasso 的總體設(shè)計(jì)圖。整個(gè)庫(kù)分為 Dispatcher爆班,RequestHandler 及 Downloader衷掷,PicassoDrawable 等模塊。
Dispatcher 負(fù)責(zé)分發(fā)和處理 Action柿菩,包括提交戚嗅、暫停、繼續(xù)枢舶、取消懦胞、網(wǎng)絡(luò)狀態(tài)變化、重試等等凉泄。
簡(jiǎn)單的講就是 Picasso 收到加載及顯示圖片的任務(wù)躏尉,創(chuàng)建 Request 并將它交給 Dispatcher,Dispatcher 分發(fā)任務(wù)到具體 RequestHandler后众,任務(wù)通過 MemoryCache 及 Handler(數(shù)據(jù)獲取接口) 獲取圖片胀糜,圖片獲取成功后通過 PicassoDrawable 顯示到 Target 中。
需要注意的是上面 Data 的 File system 部分蒂誉,Picasso 沒有自定義本地緩存的接口教藻,默認(rèn)使用 http 的本地緩存,API 9 以上使用 okhttp右锨,以下使用 Urlconnection括堤,所以如果需要自定義本地緩存就需要重定義 Downloader。
Picasso 優(yōu)點(diǎn)
(1) 自帶統(tǒng)計(jì)監(jiān)控功能
支持圖片緩存使用的監(jiān)控陡蝇,包括緩存命中率痊臭、已使用內(nèi)存大小、節(jié)省的流量等登夫。
(2) 支持優(yōu)先級(jí)處理
每次任務(wù)調(diào)度前會(huì)選擇優(yōu)先級(jí)高的任務(wù)广匙,比如 App 頁面中 Banner 的優(yōu)先級(jí)高于 Icon 時(shí)就很適用。
(3) 支持延遲到圖片尺寸計(jì)算完成加載
(4) 支持飛行模式恼策、并發(fā)線程數(shù)根據(jù)網(wǎng)絡(luò)類型而變
手機(jī)切換到飛行模式或網(wǎng)絡(luò)類型變換時(shí)會(huì)自動(dòng)調(diào)整線程池最大并發(fā)數(shù)鸦致,比如 wifi 最大并發(fā)為 4, 4g 為 3涣楷,3g 為 2分唾。
這里 Picasso 根據(jù)網(wǎng)絡(luò)類型來決定最大并發(fā)數(shù),而不是 CPU 核數(shù)狮斗。
(5) “無”本地緩存
無”本地緩存绽乔,不是說沒有本地緩存,而是 Picasso 自己沒有實(shí)現(xiàn)碳褒,交給了 Square 的另外一個(gè)網(wǎng)絡(luò)庫(kù) okhttp 去實(shí)現(xiàn)折砸,這樣的好處是可以通過請(qǐng)求 Response Header 中的 Cache-Control 及 Expired 控制圖片的過期時(shí)間看疗。
缺點(diǎn)
于不支持GIF, 并且它可能是想讓服務(wù)器去處理圖片的縮放, 它緩存的圖片是未縮放的, 并且默認(rèn)使用ARGB_8888格式緩存圖片, 緩存體積大。
Glide
總體設(shè)計(jì)及流程
上面是 Glide 的總體設(shè)計(jì)圖睦授。整個(gè)庫(kù)分為 RequestManager(請(qǐng)求管理器)两芳,Engine(數(shù)據(jù)獲取引擎)、Fetcher(數(shù)據(jù)獲取器)去枷、MemoryCache(內(nèi)存緩存)怖辆、DiskLRUCache、Transformation(圖片處理)删顶、Encoder(本地緩存存儲(chǔ))竖螃、Registry(圖片類型及解析器配置)、Target(目標(biāo))等模塊翼闹。
簡(jiǎn)單的講就是 Glide 收到加載及顯示資源的任務(wù)斑鼻,創(chuàng)建 Request 并將它交給RequestManager蒋纬,Request 啟動(dòng) Engine 去數(shù)據(jù)源獲取資源(通過 Fetcher )猎荠,獲取到后 Transformation 處理后交給 Target。
Glide 依賴于 DiskLRUCache蜀备、GifDecoder 等開源庫(kù)去完成本地緩存和 Gif 圖片解碼工作关摇。
2. Glide 優(yōu)點(diǎn)
(1) 圖片緩存->媒體緩存
Glide 不僅是一個(gè)圖片緩存,它支持 Gif碾阁、WebP输虱、縮略圖。甚至是 Video脂凶,所以更該當(dāng)做一個(gè)媒體緩存宪睹。
(2) 支持優(yōu)先級(jí)處理
(3) 與 Activity/Fragment 生命周期一致,支持 trimMemory
Glide 對(duì)每個(gè) context 都保持一個(gè) RequestManager蚕钦,通過 FragmentTransaction 保持與 Activity/Fragment 生命周期一致亭病,并且有對(duì)應(yīng)的 trimMemory 接口實(shí)現(xiàn)可供調(diào)用。
(4) 支持 okhttp嘶居、Volley
Glide 默認(rèn)通過 UrlConnection 獲取數(shù)據(jù)罪帖,可以配合 okhttp 或是 Volley 使用。實(shí)際 ImageLoader邮屁、Picasso 也都支持 okhttp整袁、Volley。
(5) 內(nèi)存友好
① Glide 的內(nèi)存緩存有個(gè) active 的設(shè)計(jì)
從內(nèi)存緩存中取數(shù)據(jù)時(shí)佑吝,不像一般的實(shí)現(xiàn)用 get坐昙,而是用 remove,再將這個(gè)緩存數(shù)據(jù)放到一個(gè) value 為軟引用的 activeResources map 中芋忿,并計(jì)數(shù)引用數(shù)炸客,在圖片加載完成后進(jìn)行判斷襟士,如果引用計(jì)數(shù)為空則回收掉。
② 內(nèi)存緩存更小圖片
Glide 以 url嚷量、viewwidth陋桂、viewheight、屏幕的分辨率等做為聯(lián)合 key蝶溶,將處理后的圖片緩存在內(nèi)存緩存中嗜历,而不是原始圖片以節(jié)省大小
③ 與 Activity/Fragment 生命周期一致,支持 trimMemory
④ 圖片默認(rèn)使用默認(rèn) RGB565 而不是 ARGB888
雖然清晰度差些抖所,但圖片更小梨州,也可配置到 ARGB_888。
其他:Glide 可以通過 signature 或不使用本地緩存支持 url 過期
Fresco
Fresco 是 Facebook 的開源的圖片緩存田轧,主要特點(diǎn)包括:
- 兩個(gè)內(nèi)存緩存加上 Native 緩存構(gòu)成了三級(jí)緩存暴匠;
- 支持流式,可以類似網(wǎng)頁上模糊漸進(jìn)式顯示圖片傻粘;
- 對(duì)多幀動(dòng)畫圖片支持更好每窖,如 Gif、WebP弦悉。
優(yōu)點(diǎn)
1. 圖片存儲(chǔ)在安卓系統(tǒng)的匿名共享內(nèi)存, 而不是虛擬機(jī)的堆內(nèi)存中, 圖片的中間緩沖數(shù)據(jù)也存放在本地堆內(nèi)存, 所以, 應(yīng)用程序有更多的內(nèi)存使用,不會(huì)因?yàn)閳D片加載而導(dǎo)致oom, 同時(shí)也減少垃圾回收器頻繁調(diào)用回收Bitmap導(dǎo)致的界面卡頓,性能更高.
2. 漸進(jìn)式加載JPEG圖片, 支持圖片從模糊到清晰加載
3. 圖片可以以任意的中心點(diǎn)顯示在ImageView, 而不僅僅是圖片的中心.
4. JPEG圖片改變大小也是在native進(jìn)行的, 不是在虛擬機(jī)的堆內(nèi)存, 同樣減少OOM
5. 很好的支持GIF圖片的顯示
缺點(diǎn)
框架較大, 影響Apk體積
使用較繁瑣
匯總
三者總體上來說窒典,ImageLoader 的功能以及代理容易理解長(zhǎng)度都一般旱物。
Picasso 代碼雖然只在一個(gè)包下澈圈,沒有嚴(yán)格的包區(qū)分怔鳖,但代碼簡(jiǎn)單最楷、邏輯清晰鸡号,一兩個(gè)小時(shí)就能叫深入的了解完筝尾。
Glide 功能強(qiáng)大途戒,但代碼量大命黔、流轉(zhuǎn)復(fù)雜良拼。
Glide vs Picasso
Glide和Picasso有90%的相似度战得,準(zhǔn)確的說,就是Picasso的克隆版本将饺。但是在細(xì)節(jié)上還是有不少區(qū)別的贡避。
導(dǎo)入庫(kù)
Picasso和Glide都在jcenter上。在項(xiàng)目中添加依賴非常簡(jiǎn)單:
Picasso
dependencies {
compile 'com.squareup.picasso:picasso:2.5.1'
}
Glide
dependencies {
compile 'com.github.bumptech.glide:glide:3.5.2'
compile 'com.android.support:support-v4:22.0.0'
}
Glide需要依賴Support Library v4予弧,別忘了刮吧。其實(shí)Support Library v4已經(jīng)是應(yīng)用程序的標(biāo)配了,這不是什么問題掖蛤。
基礎(chǔ)
就如我所說的Glide和Picasso非常相似杀捻,Glide加載圖片的方法和Picasso如出一轍。
Picasso
Picasso.with(context)
.load("http://inthecheesefactory.com/uploads/source/glidepicasso/cover.jpg")
.into(ivImg);
Glide
Glide.with(context)
.load("http://inthecheesefactory.com/uploads/source/glidepicasso/cover.jpg")
.into(ivImg);
雖然兩者看起來一樣蚓庭,但是Glide更易用致讥,因?yàn)镚lide的with方法不光接受Context仅仆,還接受Activity 和 Fragment,Context會(huì)自動(dòng)的從他們獲取垢袱。
同時(shí)將Activity/Fragment作為with()參數(shù)的好處是:圖片加載會(huì)和Activity/Fragment的生命周期保持一致墓拜,比如在Paused狀態(tài)暫停加載,在Resumed的時(shí)候又自動(dòng)重新加載请契。所以我建議傳參的時(shí)候傳遞Activity 和 Fragment給Glide咳榜,而不是Context。
默認(rèn)Bitmap格式是RGB_565
下面是加載圖片時(shí)和Picasso的比較(1920x1080 像素的圖片加載到768x432的ImageView中)
可以看到Glide加載的圖片質(zhì)量要差于Picasso(ps:我看不出來哈)爽锥,為什么涌韩?這是因?yàn)?strong>Glide默認(rèn)的Bitmap格式是RGB_565
,比ARGB_8888
格式的內(nèi)存開銷要小一半氯夷。下面是Picasso在ARGB8888下與Glide在RGB565下的內(nèi)存開銷圖(應(yīng)用自身占用了8m臣樱,因此以8為基準(zhǔn)線比較):
將Glide Bitmap格式設(shè)為ARGB_8888
如果你對(duì)默認(rèn)的RGB_565
效果還比較滿意,可以不做任何事腮考,但是如果你覺得難以接受雇毫,可以創(chuàng)建一個(gè)新的GlideModule
將Bitmap格式轉(zhuǎn)換到ARGB_8888
:
public class GlideConfiguration implements GlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
// Apply options to the builder here.
builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
}
@Override
public void registerComponents(Context context, Glide glide) {
// register ModelLoaders here.
}
}
同時(shí)在AndroidManifest.xml中將GlideModule定義為meta-data
<meta-data android:name="com.inthecheesefactory.lab.glidepicasso.GlideConfiguration"
android:value="GlideModule"/>
這樣看起來就會(huì)好很多。
內(nèi)存開銷對(duì)比
我們?cè)賮砜纯磧?nèi)存開銷圖秸仙,這次貌似Glide花費(fèi)了兩倍于上次的內(nèi)存嘴拢,但是Picasso的內(nèi)存開銷仍然遠(yuǎn)大于Glide桩盲。
原因在于:
Picasso是加載了全尺寸的圖片到內(nèi)存寂纪,然后讓GPU來實(shí)時(shí)重繪大小赌结;
Glide加載的大小和ImageView的大小是一致的捞蛋,因此更小。
當(dāng)然柬姚,Picasso也可以指定加載的圖片大小的:
Picasso.with(this)
.load("http://nuuneoi.com/uploads/source/playstore/cover.jpg")
.resize(768, 432)
.into(ivImgPicasso);
但是問題在于你需要主動(dòng)計(jì)算ImageView的大小拟杉,或者說你的ImageView大小是具體的值(而不是wrap_content),你也可以這樣:
Picasso.with(this)
.load("http://nuuneoi.com/uploads/source/playstore/cover.jpg")
.fit()
.centerCrop()
.into(ivImgPicasso);
現(xiàn)在Picasso的內(nèi)存開銷就和Glide差不多了量承。
雖然內(nèi)存開銷差距不到搬设,但是在這個(gè)問題上Glide完勝Picasso。因?yàn)镚lide可以自動(dòng)計(jì)算出任意情況下的ImageView大小撕捍。
Image質(zhì)量的細(xì)節(jié)
這是將ImageView還原到真實(shí)大小時(shí)的比較拿穴。
你可以看到,Glide加載的圖片沒有Picasso那么平滑忧风,我還沒有找到一個(gè)可以直觀改變圖片大小調(diào)整算法的方法默色。
但是這并不算什么壞事,因?yàn)楹茈y察覺狮腿。
磁盤緩存
Picasso和Glide在磁盤緩存策略上有很大的不同腿宰。Picasso緩存的是全尺寸的呕诉,而Glide緩存的是跟ImageView尺寸相同的。
上面提到的平滑度的問題依然存在吃度,而且如果加載的是RGB565圖片甩挫,那么緩存中的圖片也是RGB565。
我 嘗試將ImageView調(diào)整成不同大小椿每,但不管大小如何Picasso只緩存一個(gè)全尺寸的捶闸。Glide則不同,它會(huì)為每種大小的ImageView緩存 一次拖刃。盡管一張圖片已經(jīng)緩存了一次删壮,但是假如你要在另外一個(gè)地方再次以不同尺寸顯示,需要重新下載兑牡,調(diào)整成新尺寸的大小央碟,然后將這個(gè)尺寸的也緩存起來。
具體說來就是:假如在第一個(gè)頁面有一個(gè)200x200的ImageView均函,在第二個(gè)頁面有一個(gè)100x100的ImageView亿虽,這兩個(gè)ImageView本來是要顯示同一張圖片,卻需要下載兩次苞也。
不過洛勉,你可以改變這種行為,讓Glide既緩存全尺寸又緩存其他尺寸:
Glide.with(this)
.load("http://nuuneoi.com/uploads/source/playstore/cover.jpg")
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(ivImgGlide);
下次在任何ImageView中加載圖片的時(shí)候如迟,全尺寸的圖片將從緩存中取出收毫,重新調(diào)整大小,然后緩存殷勘。
Glide的這種方式優(yōu)點(diǎn)是加載顯示非炒嗽伲快。而Picasso的方式則因?yàn)樾枰陲@示之前重新調(diào)整大小而導(dǎo)致一些延遲玲销,即便你添加了這段代碼來讓其立即顯示:
//Picasso.noFade();
Picasso和Glide各有所長(zhǎng)输拇,你根據(jù)自己的需求選擇合適的。
對(duì)我而言贤斜,我更喜歡Glide策吠,因?yàn)樗h(yuǎn)比Picasso快,雖然需要更大的空間來緩存瘩绒。
特性
你可以做到幾乎和Picasso一樣多的事情猴抹,代碼也幾乎一樣。
Image Resizing
// Picasso.resize(300, 200);
// Glide.override(300, 200);
Center Cropping
// Picasso.centerCrop();
// Glide.centerCrop();
Transforming
// Picasso.transform(new CircleTransform())
// Glide.transform(new CircleTransform(context))
設(shè)置占位圖或者加載錯(cuò)誤圖:
// Picasso.placeholder(R.drawable.placeholder).error(R.drawable.imagenotfound)
// Glide.placeholder(R.drawable.placeholder).error(R.drawable.imagenotfound)
幾乎和Picasso一樣草讶,從Picasso轉(zhuǎn)換到Glide對(duì)你來說就是小菜一碟洽糟。
Glide可以做而Picasso 做不到
-
Glide可以加載GIF動(dòng)態(tài)圖,而Picasso不能。
.
同時(shí)因?yàn)镚lide和Activity/Fragment的生命周期是一致的坤溃,因此gif的動(dòng)畫也會(huì)自動(dòng)的隨著Activity/Fragment的狀態(tài)暫停拍霜、重放。Glide 的緩存在gif這里也是一樣薪介,調(diào)整大小然后緩存祠饺。
但是從我的一次測(cè)試結(jié)果來看Glide 動(dòng)畫會(huì)消費(fèi)太多的內(nèi)存,因此謹(jǐn)慎使用汁政。 除了gif動(dòng)畫之外道偷,Glide還可以將任何的本地視頻解碼成一張靜態(tài)圖片。
還有一個(gè)特性是你可以配置圖片顯示的動(dòng)畫记劈,而Picasso只有一種動(dòng)畫:fading in勺鸦。
最后一個(gè)是可以使用thumbnail()產(chǎn)生一個(gè)你所加載圖片的thumbnail(縮略圖)。
其實(shí)還有一些特性目木,不過不是非常重要换途,比如將圖像轉(zhuǎn)換成字節(jié)數(shù)組等。
配置
有許多可以配置的選項(xiàng)刽射,比如大小军拟,緩存的磁盤位置,最大緩存空間誓禁,位圖格式等等懈息。可以在這個(gè)頁面查看這些配置 Configuration
摹恰。
庫(kù)的大小
Picasso (v2.5.1)的大小約118kb辫继,而Glide (v3.5.2)的大小約430kb。
Anyway 312KB difference might not be that significant.
不過312kb的差距并不是很重要戒祠。
Picasso和Glide的方法個(gè)數(shù)分別是840和2678個(gè)骇两。
必須指出,對(duì)于DEX文件65535個(gè)方法的限制來說姜盈,2678是一個(gè)相當(dāng)大的數(shù)字了。建議在使用Glide的時(shí)候開啟ProGuard配阵。
總結(jié)
Glide加載圖像以及磁盤緩存的方式都要優(yōu)于Picasso馏颂,速度更快,并且Glide更有利于減少OutOfMemoryError的發(fā)生棋傍,GIF動(dòng)畫是Glide的殺手锏救拉。
不過Picasso的圖片質(zhì)量更高。
雖然我使用了很長(zhǎng)時(shí)間的Picasso瘫拣,但是我得承認(rèn)現(xiàn)在我更喜歡Glide亿絮。我的建議是使用Glide,但是將Bitmap格式換成 ARGB_8888、讓Glide緩存同時(shí)緩存全尺寸和改變尺寸兩種派昧。