一、UniversalImageLoader
https://github.com/nostra13/Android-Universal-Image-Loader
UIL可以算是老牌最火的圖片加載庫了,使用過這個框架的項目可以說多到教你做人,我第一次把第三方開源圖片加載框架加入項目中的就是這個了,當時感覺瞬間逼格上漲,媽媽再也不用擔心出現(xiàn)OOM和ListView圖片錯亂了??上У氖窃撟髡咴陧椖恐姓f明已經(jīng)停止了對該項目的維護。這就意味著以后任何的 bug 都不會修復,任何的新特性都不會再繼續(xù)開發(fā),所以毫無疑問 UIL 不推薦在項目中使用了。
使用方法:
1、在Application全局變量中的進行配置ImageLoaderConfiguration,有選擇性的進行配置,具體代碼如下:
ImageLoaderConfiguration config =newImageLoaderConfiguration.Builder(context)
.memoryCacheExtraOptions(480, 800)// default = device screen dimensions
.discCacheExtraOptions(480, 800, CompressFormat.JPEG, 75)
.taskExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
.taskExecutorForCachedImages(AsyncTask.THREAD_POOL_EXECUTOR)
.threadPoolSize(3)// default線程池數(shù)量
.threadPriority(Thread.NORM_PRIORITY - 1)// default
.tasksProcessingOrder(QueueProcessingType.FIFO)// default
.denyCacheImageMultipleSizesInMemory()
.memoryCache(newLruMemoryCache(2 * 1024 * 1024))//內(nèi)存緩存
.memoryCacheSize(2 * 1024 * 1024)
.discCache(newUnlimitedDiscCache(cacheDir))// 磁盤緩存
.discCacheSize(50 * 1024 * 1024)
.discCacheFileCount(100)
.discCacheFileNameGenerator(newHashCodeFileNameGenerator())// default
.imageDownloader(newBaseImageDownloader(context))// default
.imageDecoder(newBaseImageDecoder())// default
.defaultDisplayImageOptions(DisplayImageOptions.createSimple())// default
.enableLogging()
.build();
2、針對每次加載任務進行配置DisplayImageOptions
DisplayImageOptions options =newDisplayImageOptions.Builder()
.showStubImage(R.drawable.ic_stub)
.showImageForEmptyUri(R.drawable.ic_empty)
.showImageOnFail(R.drawable.ic_error)
.resetViewBeforeLoading()
.delayBeforeLoading(1000)
.cacheInMemory()
.cacheOnDisc()
.preProcessor(...)
.postProcessor(...)
.extraForDownloader(...)
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2)// default
.bitmapConfig(Bitmap.Config.ARGB_8888)// default
.decodingOptions(...)
.displayer(newSimpleBitmapDisplayer())// default
.handler(newHandler())// default
.build();
UIL支持的圖片加載格式如下:
String imageUri ="http://site.com/image.png"; // from Web
String imageUri ="file:///mnt/sdcard/image.png"; // from SD card
String imageUri ="content://media/external/audio/albumart/13"; // from content provider
String imageUri ="assets://image.png"; // from assets
String imageUri ="drawable://"+ R.drawable.image; // from drawables (only images, non-9patch)
配置好后調(diào)用imageLoader.displayImage方法就OK了,妥妥的!
下面簡單分析一下,UIL框架的加載原理:
1、ImageLoader圖片加載器,對外的主要 API,采取了單例模式,用于圖片的加載和顯示。
2、MemoryCache圖片內(nèi)存換成。默認使用了 LRU 算法。 LRU: Least Recently Used 近期最少使用算法, 選用了基于鏈表結(jié)構(gòu)的 LinkedHashMap 作為存儲結(jié)構(gòu)。假設情景:內(nèi)存緩存設置的閾值只夠存儲兩個 bitmap 對象,當 put 第三個 bitmap 對象時,將近期最少使用的 bitmap 對象移除。
3、DiskCache圖片磁盤緩存,默認使用LruDiskCache算法,在緩存滿時刪除最近最少使用的圖片;緩存目錄下名為journal的文件記錄緩存的所有操作
4、圖片加載流程
1.判斷圖片的內(nèi)存緩存是否存在,若存在直接執(zhí)行步驟 8;
2.判斷圖片的磁盤緩存是否存在,若存在直接執(zhí)行步驟 5;
3.ImageDownloader從網(wǎng)絡上下載圖片;
4.將圖片緩存在磁盤上;
5.ImageDecoder將圖片 decode 成 bitmap 對象;
6.BitmapProcessor根據(jù)DisplayImageOptions配置對圖片進行預處理(Pre-process Bitmap);
7.將 bitmap 對象緩存到內(nèi)存中;
8.根據(jù)DisplayImageOptions配置對圖片進行后處理(Post-process Bitmap);
9.執(zhí)行DisplayBitmapTask將圖片顯示在相應的控件上。
二、Picasso
https://github.com/square/picasso
Picasso是Square公司開源的一個Android平臺上的圖片加載框架,簡單易用,一句話搞定項目中的圖片加載,好用到令人發(fā)指。
使用一句話:Picasso.with(this).load("url").placeholder(R.mipmap.ic_default).into(imageView);
原理簡要分析:
1、Picasso.with(Context):入手
public static Picasso with(Contextcontext){
? ? ? ? ? ?if(singleton==null){
? ? ? ? ? ? ? ? ? ?synchronized(Picasso.class){
? ? ? ? ? ? ? ? ? ? ? if(singleton==null){
? ? ? ? ? ? ? ? ? ? ? ? ?singleton=newBuilder(context).build();
? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ?}
? ? ? ? ? ?}
return singleton;}
單列模式,保證多線程情況下,也只有一個實例。
/** Create the {@link Picasso} instance. 創(chuàng)建Picasso的實例 */
public Picassobuild(){
Context context=this.context;
if(downloader==null){
downloader=Utils.createDefaultDownloader(context);
}
if(cache==null){
cache=new LruCache(context);
}
if(service==null){
service=new PicassoExecutorService();
}
if(transformer==null){
transformer=RequestTransformer.IDENTITY;
}
Stats stats=newStats(cache);
Dispatcher dispatcher=new Dispatcher(context,service,HANDLER,downloader,cache,stats);
return new Picasso(context,dispatcher,cache,listener,transformer,
requestHandlers,stats,indicatorsEnabled,loggingEnabled);
}
默認初始化了以下的參數(shù):
Downloader
DownLoader就是下載用的工具類,在Picasso當中,如果OKHttp可以使用的話,就會默認使用OKHttp,如果無法使用的話,就會使用UrlConnectionDownloader(默認使用HttpURLConnection實現(xiàn))。
Cache
默認實現(xiàn)為LruCache,就是使用LinkedHashMap實現(xiàn)的一個Cache類,注意的一個地方就是,在其他的地方,我們一般默認的是限制的capacity,但是這個地方我們是限制的總共使用的內(nèi)存空間。因此LruCache在實現(xiàn)的時候,其實簡單理解就是將LinkedHashMap封裝,然后基于LinkedHashMap的方法實現(xiàn)Cache的方法,在Cache的set()方法的時候,會不斷計算當前還可以使用的空間大小,要是超出范圍,則刪除之前保存的數(shù)據(jù)。
ExecutorService
默認的實現(xiàn)為PicassoExecutorService,該類也比較簡單,其實就是ThreadPoolExecutor,在其功能的基礎上繼續(xù)封裝,在其中有一個比較細心的功能就是,Picasso通過PicassoExecutorService設置線程數(shù)量,來調(diào)整在2G/3G/4G/WiFi不同網(wǎng)絡情況下的不同表現(xiàn)。
RequestTransformer
ReqeustTransformer是一個接口,用來預處理Reqeust,可以用來將請求進行預先處理,比如改個域名啥的。
Stats
主要是一些統(tǒng)計信息,比如cache hit/miss,總共下載的文件大小,下載過的圖片數(shù)量,轉(zhuǎn)換的圖片數(shù)量等等。
Dispatcher
Picasso當中,分發(fā)任務的線程,這是我們以后要重點研究的一個類,先標記一下,這個Dispatcher主要做了以下的事情:
啟動了一個DispatcherThread線程初始化了一個用來處理消息的DispatcherHandler,注意,根據(jù)Dispatcher中默認配置,該Handler所有數(shù)據(jù)的處理是在DispatcherThread之上。初始化并注冊了一個網(wǎng)絡狀態(tài)廣播接收器。
2、圖片加載流程:
1.初始化Picasso,實例化其唯一的對象。
2.根據(jù)傳入的Url、File、resource Id,構(gòu)建ReqeustCreator對象
3.根據(jù)ReqeustCreator構(gòu)建Request對象,同時根據(jù)Reqeust屬性,嘗試從Cache中訪問數(shù)據(jù)
4.Cache Hit,則通過回調(diào),設置Target或者ImageView,完成該Reqeust
5.如果Cache Miss,那么則構(gòu)建相應的Action,并提交到DispatcherThread當中。
6.Dispatcher中的Handler接收到相應的Message,調(diào)用dispatcher.performSubmit(action)進行處理。
7.創(chuàng)建BitmapHunter對象,并提交到PicassoExecutorService線程池
8.再次檢查Memory Cache中已經(jīng)有緩存,如果Hit,則讀取緩存中的Bitmap
9.如果Cache miss,則交給Action對應的ReqeustHandler進行處理,比如網(wǎng)絡請求,或者從File讀取圖片
10.返回結(jié)果之后,通知Dispatcher中的Handler處理結(jié)果。
11.DispatcherThread中將BitmapHunter的結(jié)果打包(batch),最快200ms打包一次。通知主線程HANDLER進行處理
12.主線程HANDLER接收打包的BitmapHunter,對最后的結(jié)果進行分發(fā)。
注意:Picasso框架沒有實現(xiàn)磁盤緩存,配合OkHttp進行實現(xiàn)。
三、Glide
https://github.com/bumptech/glide
Glide 是 Google 一位員工的大作,他完全是基于 Picasso 的,沿襲了 Picasso 的簡潔風格,但是在此做了大量優(yōu)化與改進。
Glide 默認的 Bitmap 格式是 RGB_565 格式,而 Picasso 默認的是 ARGB_8888 格式,這個內(nèi)存開銷要小一半。
在磁盤緩存方面,Picasso 只會緩存原始尺寸的圖片,而 Glide 緩存的是多種規(guī)格,也就意味著 Glide 會根據(jù)你 ImageView 的大小來緩存相應大小的圖片尺寸,比如你 ImageView 大小是200*200,原圖是 400*400 ,而使用 Glide 就會緩存 200*200 規(guī)格的圖,而 Picasso 只會緩存 400*400 規(guī)格的。這個改進就會導致 Glide 比 Picasso 加載的速度要快,畢竟少了每次裁剪重新渲染的過程。
最重要的一個特性是 Glide 支持加載 Gif 動態(tài)圖,而 Picasso 不支持該特性。
除此之外,還有很多其他配置選項的增加。
總體來說,Glide 是在 Picasso 基礎之上進行的二次開發(fā),各個方面做了不少改進,不過這也導致他的包比 Picasso 大不少,不過也就不到 500k,Picasso 是100多k,方法數(shù)也比 Picasso 多不少,不過畢竟級別還是蠻小的,影響不是很大。
四、Fresco
https://github.com/facebook/fresco
Fresco 是 Facebook 出品,他是新一代的圖片加載庫,我們知道 Android 應用程序可用的內(nèi)存有限,經(jīng)常會因為圖片加載導致 OOM,雖然我們有各種手段去優(yōu)化,盡量減少出現(xiàn) OOM 的可能性,但是永遠沒法避免,尤其某些低端手機 OOM 更是嚴重。而 Facebook 就另辟蹊徑,既然沒法在 Java 層處理,我們就在更底層的 Native 堆做手腳。于是 Fresco 將圖片放到一個特別的內(nèi)存區(qū)域叫 Ashmem 區(qū),就是屬于 Native 堆,圖片將不再占用 App 的內(nèi)存,Java 層對此無能為力,這里是屬于 C++ 的地盤,所以能大大的減少 OOM。
本人四個庫都使用了一遍,對比到Fresco確實強大,加載大圖Fresco最屌,有的圖Glide和Picasso加載不出來,換上Fresco妥妥的,不過Fresco比較龐大,推薦在主要都是圖片的app中使用,一般的app使用Glide和Picasso就夠了!