Universal-Image-Loader解析系列
Universal-Image-Loader解析(一)基本介紹與使用
Universal-Image-Loader解析(二)內(nèi)部緩存原理
Universal-Image-Loader解析(三)源代碼解析
基本介紹
相信大家平時(shí)做Android應(yīng)用的時(shí)候肥卡,多少會(huì)接觸到異步加載圖片,或者加載大量圖片的問(wèn)題批销,而加載圖片我們常常會(huì)遇到許多的問(wèn)題,比如說(shuō)圖片的錯(cuò)亂蒜哀,OOM等問(wèn)題戳寸,對(duì)于新手來(lái)說(shuō),這些問(wèn)題解決起來(lái)會(huì)比較吃力捂掰,所以就有很多的開(kāi)源圖片加載框架應(yīng)運(yùn)而生贝椿,比較著名的就是Universal-Image-Loader想括,相信很多朋友都聽(tīng)過(guò)或者使用過(guò)這個(gè)強(qiáng)大的圖片加載框架,今天這篇文章就是對(duì)這個(gè)框架的基本介紹以及使用烙博,主要是幫助那些沒(méi)有使用過(guò)這個(gè)框架的朋友們瑟蜈。該項(xiàng)目存在于Github上面Android-Universal-Image-Loader,我們可以先看看這個(gè)開(kāi)源庫(kù)存在哪些特征
多線程下載圖片渣窜,圖片可以來(lái)源于網(wǎng)絡(luò)踪栋,文件系統(tǒng),項(xiàng)目文件夾assets中以及drawable中等
支持隨意的配置ImageLoader图毕,例如線程池,圖片下載器眷唉,內(nèi)存緩存策略予颤,硬盤(pán)緩存策略,圖片顯示選項(xiàng)以及其他的一些配置
支持圖片的內(nèi)存緩存冬阳,文件系統(tǒng)緩存或者SD卡緩存
支持圖片下載過(guò)程的監(jiān)聽(tīng)
根據(jù)控件(ImageView)的大小對(duì)Bitmap進(jìn)行裁剪蛤虐,減少Bitmap占用過(guò)多的內(nèi)存
較好的控制圖片的加載過(guò)程,例如暫停圖片加載肝陪,重新開(kāi)始加載圖片驳庭,一般使用在 ListView,GridView中,滑動(dòng)過(guò)程中暫停加載圖片氯窍,停止滑動(dòng)的時(shí)候去加載圖片
提供在較慢的網(wǎng)絡(luò)下對(duì)圖片進(jìn)行加載
當(dāng)然上面列舉的特性可能不全饲常,要想了解一些其他的特性只能通過(guò)我們的使用慢慢去發(fā)現(xiàn)了
ImageLoaderConfiguration
圖片加載器ImageLoader的配置參數(shù),使用Builder模式狼讨。
常用的配置屬性有
//通過(guò)StorageUtils獲取內(nèi)置的內(nèi)存目錄/data/data/.../cache
File cacheDir = StorageUtils.getCacheDirectory(context);
ImageLoaderConfiguration config = new ImageLoaderConfiguration
.Builder(getApplicationContext())
.memoryCacheExtraOptions(480, 800) //即保存的每個(gè)緩存文件的最大長(zhǎng)寬
.threadPoolSize(3) //線程池內(nèi)加載的數(shù)量
.threadPriority(Thread.NORM_PRIORITY - 2)
//解釋?zhuān)寒?dāng)同一個(gè)Uri獲取不同大小的圖片贝淤,緩存到內(nèi)存時(shí),只緩存一個(gè)政供。默認(rèn)會(huì)緩存多個(gè)不同的大小的相同圖片
.denyCacheImageMultipleSizesInMemory() //拒絕緩存多個(gè)圖片播聪。
.memoryCache(new WeakMemoryCache()) //緩存策略你可以通過(guò)自己的內(nèi)存緩存實(shí)現(xiàn) 朽基,這里用弱引用,缺點(diǎn)是太容易被回收了离陶,不是很好稼虎!
.memoryCacheSize(2 * 1024 * 1024) //設(shè)置內(nèi)存緩存的大小
.diskCacheSize(50 * 1024 * 1024) //設(shè)置磁盤(pán)緩存大小 50M
.diskCacheFileNameGenerator(new Md5FileNameGenerator()) //將保存的時(shí)候的URI名稱(chēng)用MD5 加密
.tasksProcessingOrder(QueueProcessingType.LIFO) //設(shè)置圖片下載和顯示的工作隊(duì)列排序
.diskCacheFileCount(100) //緩存的文件數(shù)量
.diskCache(new UnlimitedDiskCache(cacheDir)) //自定義緩存路徑
.defaultDisplayImageOptions(defaultOptions) //顯示圖片的參數(shù),默認(rèn):DisplayImageOptions.createSimple()
.imageDownloader(new BaseImageDownloader(this, 5 * 1000, 30 * 1000)) // connectTimeout (5 s), readTimeout (30 s)超時(shí)時(shí)間
.writeDebugLogs() //打開(kāi)調(diào)試日志
.build();//開(kāi)始構(gòu)建
//配置使用
ImageLoader.getInstance().init(configuration);
可以設(shè)置內(nèi)存緩存招刨,硬盤(pán)緩存的相關(guān)參數(shù)等霎俩。
設(shè)置完相關(guān)的參數(shù)后就可進(jìn)行圖片加載顯示
圖片加載
ImageLader提供了幾個(gè)圖片加載的方法,主要是這幾個(gè)displayImage(), loadImage(),loadImageSync()计济,loadImageSync()方法是同步的茸苇,android4.0有個(gè)特性,網(wǎng)絡(luò)操作不能在主線程沦寂,所以loadImageSync()方法我們就不去使用
loadimage()加載圖片
我們先使用ImageLoader的loadImage()方法來(lái)加載網(wǎng)絡(luò)圖片
final ImageView mImageView = (ImageView) findViewById(R.id.image);
String imageUrl = "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s1024/A%252520Photographer.jpg";
ImageLoader.getInstance().loadImage(imageUrl, new ImageLoadingListener() {
@Override
public void onLoadingStarted(String imageUri, View view) {
}
@Override
public void onLoadingFailed(String imageUri, View view,
FailReason failReason) {
}
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
mImageView.setImageBitmap(loadedImage);
}
@Override
public void onLoadingCancelled(String imageUri, View view) {
}
});
傳入圖片的url和ImageLoaderListener, 在回調(diào)方法onLoadingComplete()中將loadedImage設(shè)置到ImageView上面就行了学密,如果你覺(jué)得傳入ImageLoaderListener太復(fù)雜了,我們可以使用SimpleImageLoadingListener類(lèi)传藏,該類(lèi)提供了ImageLoaderListener接口方法的空實(shí)現(xiàn)腻暮,使用的是缺省適配器模式
final ImageView mImageView = (ImageView) findViewById(R.id.image);
String imageUrl = "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s1024/A%252520Photographer.jpg";
ImageLoader.getInstance().loadImage(imageUrl, new SimpleImageLoadingListener(){
@Override
public void onLoadingComplete(String imageUri, View view,
Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
mImageView.setImageBitmap(loadedImage);
}
});
如果我們要指定圖片的大小該怎么辦呢,這也好辦毯侦,初始化一個(gè)ImageSize對(duì)象哭靖,指定圖片的寬和高,代碼如下
final ImageView mImageView = (ImageView) findViewById(R.id.image);
String imageUrl = "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s1024/A%252520Photographer.jpg";
ImageSize mImageSize = new ImageSize(100, 100);
ImageLoader.getInstance().loadImage(imageUrl, mImageSize, new SimpleImageLoadingListener(){
@Override
public void onLoadingComplete(String imageUri, View view,
Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
mImageView.setImageBitmap(loadedImage);
}
});
上面只是很簡(jiǎn)單的使用ImageLoader來(lái)加載網(wǎng)絡(luò)圖片侈离,在實(shí)際的開(kāi)發(fā)中试幽,我們并不會(huì)這么使用,那我們平常會(huì)怎么使用呢卦碾?我們會(huì)用到DisplayImageOptions铺坞,他可以配置一些圖片顯示的選項(xiàng),比如圖片在加載中ImageView顯示的圖片洲胖,是否需要使用內(nèi)存緩存济榨,是否需要使用文件緩存等等
DisplayImageOptions
可以配置一些圖片顯示的選項(xiàng),比如圖片在加載中ImageView顯示的圖片绿映,是否需要使用內(nèi)存緩存擒滑,是否需要使用文件緩存等等,可供我們選擇的配置如下
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic_stub) // resource or drawable
.showImageForEmptyUri(R.drawable.ic_empty) // resource or drawable
.showImageOnFail(R.drawable.ic_error) // resource or drawable
.resetViewBeforeLoading(false) // default
.delayBeforeLoading(1000)
.cacheInMemory(false) // default
.cacheOnDisk(false) // default
.preProcessor(...)
.postProcessor(...)
.extraForDownloader(...)
.considerExifParams(false) // default
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default
.bitmapConfig(Bitmap.Config.ARGB_8888) // default
.decodingOptions(...)
.displayer(new SimpleBitmapDisplayer()) // default
.handler(new Handler()) // default
.build();
大家就可以根據(jù)實(shí)際情況去設(shè)置叉弦。
displayImage()加載圖片
接下來(lái)我們就來(lái)看看網(wǎng)絡(luò)圖片加載的另一個(gè)方法displayImage()丐一,代碼如下
String imageUrl = "https://lh6.googleusercontent.com/-55osAWw3x0Q/URquUtcFr5I/AAAAAAAAAbs/rWlj1RUKrYI/s1024/A%252520Photographer.jpg";
//顯示圖片的配置
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic_stub)
.showImageOnFail(R.drawable.ic_error)
.cacheInMemory(true)
.cacheOnDisk(true)
.bitmapConfig(Bitmap.Config.RGB_565)
.build();
ImageLoader.getInstance().displayImage(imageUrl, mImageView, options);
可以看到這里是直接傳遞了ImageView進(jìn)行設(shè)置顯示,并不需要監(jiān)聽(tīng)后設(shè)置淹冰,這樣更為簡(jiǎn)便钝诚,這也是displayImage
和loadImage
的區(qū)別。
加載其他來(lái)源的圖片
使用Universal-Image-Loader框架不僅可以加載網(wǎng)絡(luò)圖片榄棵,還可以加載sd卡中的圖片凝颇,Content provider等潘拱,使用也很簡(jiǎn)單,只是將圖片的url稍加的改變下就行了拧略,下面是加載文件系統(tǒng)的圖片
我們只需要給每個(gè)圖片來(lái)源的地方加上Scheme包裹起來(lái)(Content provider除外)芦岂,然后當(dāng)做圖片的url傳遞到imageLoader中,Universal-Image-Loader框架會(huì)根據(jù)不同的Scheme獲取到輸入流
//圖片來(lái)源于文件
String imagePath = "/mnt/sdcard/image.png";
String imageUrl = Scheme.FILE.wrap(imagePath);
//相當(dāng)于file:/mnt/sdcard/image.png
//圖片來(lái)源于Content provider
String contentprividerUrl = "content://media/external/audio/albumart/13";
//圖片來(lái)源于assets
String assetsUrl = Scheme.ASSETS.wrap("image.png");
//圖片來(lái)源于
String drawableUrl = Scheme.DRAWABLE.wrap("R.drawable.image");
獲取到對(duì)應(yīng)URL后就可以調(diào)用display/loadImage方法進(jìn)行顯示垫蛆。
GirdView,ListView加載圖片
相信大部分人都是使用GridView禽最,ListView來(lái)顯示大量的圖片,而當(dāng)我們快速滑動(dòng)GridView袱饭,ListView川无,我們希望能停止圖片的加載,而在GridView虑乖,ListView停止滑動(dòng)的時(shí)候加載當(dāng)前界面的圖片懦趋,這個(gè)框架當(dāng)然也提供這個(gè)功能,使用起來(lái)也很簡(jiǎn)單疹味,它提供了PauseOnScrollListener這個(gè)類(lèi)來(lái)控制ListView,GridView滑動(dòng)過(guò)程中停止去加載圖片仅叫,該類(lèi)使用的是代理模式
listView.setOnScrollListener(new PauseOnScrollListener(imageLoader, pauseOnScroll, pauseOnFling));
gridView.setOnScrollListener(new PauseOnScrollListener(imageLoader, pauseOnScroll, pauseOnFling));
第一個(gè)參數(shù)就是我們的圖片加載對(duì)象ImageLoader,
第二個(gè)是控制是否在滑動(dòng)過(guò)程中暫停加載圖片,如果需要暫停傳true就行了糙捺,
第三個(gè)參數(shù)控制猛的滑動(dòng)界面的時(shí)候圖片是否加載
OutOfMemoryError
雖然這個(gè)框架有很好的緩存機(jī)制诫咱,有效的避免了OOM的產(chǎn)生,一般的情況下產(chǎn)生OOM的概率比較小洪灯,但是并不能保證OutOfMemoryError永遠(yuǎn)不發(fā)生坎缭,這個(gè)框架對(duì)于OutOfMemoryError做了簡(jiǎn)單的catch,保證我們的程序遇到OOM而不被crash掉,但是如果我們使用該框架經(jīng)常發(fā)生OOM签钩,我們應(yīng)該怎么去改善呢幻锁?
減少線程池中線程的個(gè)數(shù),在ImageLoaderConfiguration中的(.threadPoolSize)中配置边臼,推薦配置1-5
在DisplayImageOptions選項(xiàng)中配置bitmapConfig為Bitmap.Config.RGB_565,因?yàn)槟J(rèn)是ARGB_8888假消, 使用RGB_565會(huì)比使用ARGB_8888少消耗2倍的內(nèi)存
在ImageLoaderConfiguration中配置圖片的內(nèi)存緩存為memoryCache(new WeakMemoryCache()) 或者不使用內(nèi)存緩存
在DisplayImageOptions選項(xiàng)中設(shè)置.imageScaleType(ImageScaleType.IN_SAMPLE_INT)或者imageScaleType(ImageScaleType.EXACTLY)
參考資料
Android 開(kāi)源框架Universal-Image-Loader完全解析(一)--- 基本介紹及使用
Android-Universal-Image-Loader