ImageLoader
UIL–Universal-Image-Loader的特征及作用
- 1.多线程下载图片,图片可以来源于网络,文件系统,项目文件夹assets中以及drawable中等
- 2.支持随意的配置ImageLoader,例如线程池,图片下载器,内存缓存策略,硬盘缓存策略,图片显示选项以及其 他的一些配置
- 3.支持图片的内存缓存,文件系统缓存或者SD卡缓存
- 4.支持图片下载过程的监听
- 5.根据控件(ImageView)的大小对Bitmap进行裁剪,减少Bitmap占用过多的内存
- 6.较好的控制图片的加载过程,例如暂停图片加载,重新开始加载图片,一般使用在ListView,GridView中,滑动 过程中暂停加载图片,停止滑动的时候去加载图片
- 7.提供在较慢的网络下对图片进行加载
1、配置ImageLoader环境
下载jar包->复制到libs文件夹中(调到project窗口)->project structure 中加入这个jar包
(参考Butterknife包,注:加号选第二个)
2、简单的下载网络图片并加载到项目的ImageView控件中
1、在activity中创建一个ImageView控件
2、创建一个class (例MyAppc)继承Application
- 重写onCreate方法 在方法内配置imageloader的参数及初始化imageloader
#
//创建默认的imageloader配置参数,createDefault为使用的默认设置
ImageLoaderConfiguration configuration = ImageLoaderConfiguration.createDefault(this);
//初始化imageloader及配置
ImageLoader.getInstance().init(configuration);
3、配置清单文件
- 配置INTERNET和WRITE权限,并把Appc 配置到application的name中
4、将网络中的图片加载到 ImageView控件
- 1.获得网址:String url = “http://172.31.5.59:8080/IMG_20120825_103900.jpg“;
- 2.这里我直接使用SimpleImageLoadingListener来set设置图片
- 注:imageloader的初始化 可以在Activity中进行,并不一定要在Application中进行
//ImageSize用来设置图片的长和宽,如果不设置,可以不写,在loadImage中可以不传
ImageSize imageSize = new ImageSize(200, 280);
ImageLoader.getInstance().loadImage(url,imageSize,new SimpleImageLoadingListener(){
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
super.onLoadingComplete(imageUri, view, loadedImage);
iv.setImageBitmap(loadedImage);
}
});
3、网络图片缓存
- 1.上面介绍了一些简单的网络图片加载到ImageView控件,但是并没有缓存,这样每次运行的话,都需要从网络去加载图片,很是麻烦,所以引入了 图片的缓存技术。我们使用DisplayImage()方法来实现图片的缓存
- 内存缓存:cacheInMemory(false)
- 文件缓存:cacheOnDisk(false)
- 上面2个默认的都是false,当需要缓存的时候,把false改为true
//下面的代码写在Application类内表示全局默认图片显示方式,写在Activity类内表示局部图片显示方式
//设置Bitmap.Config.RGB_565减少内存消耗,默认的为ARGB_8888消耗的内存为RGB_565的2倍
DisplayImageOptions options = new DisplayImageOptions.Builder().cacheInMemory(true).cacheOnDisk(true).bitmapConfig(Bitmap.Config.RGB_565).build();
//当然你也可以这么写,来创建一个默认的简单的DisplayImageOptions
DisplayImageOptions options = DisplayImageOptions.createSimple();
#
//下面的代码一般写的Application类的onCreate()方法内
//自定义缓存文件路径
File cacheDir = StorageUtils.getOwnCacheDirectory(getApplicationContext(), “imageloader/Cache”);
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
//第一个参数是主文件,第二个参数是备用文件,默认为null(选填),当主文件无效时,启用备用文件
.diskCache(new UnlimitedDiskCache(cacheDir,null))
.denyCacheImageMultipleSizesInMemory()//配置多个尺寸的图片缓存
.diskCacheFileNameGenerator(new Md5FileNameGenerator())//为图片缓存配置建造者
.tasksProcessingOrder(QueueProcessingType.LIFO)//默认,为图片下载和显示配置队列处理类型
.writeDebugLogs()//打印Log日志
.defaultDisplayImageOptions(options)//为初始化配置默认的图片显示方式
.build();
ImageLoader.getInstance().init(config);//初始化ImageLoader实例
- 2.有些图片比较大的时候,加载用的时间比较长,甚至加载失败。这时候,我们需要加入2个提示的图片帮助用户直观的查看出来:
- 加载中:showImageOnLoading();
- 加载失败:showImageOnFail();
- 注:括号内填写对应显示的图片
#
DisplayImageOptions options = new DisplayImageOptions.Builder().showImageOnLoading(R.drawable.load).showImageOnFail(R.drawable.load_error)
.cacheInMemory(true).cacheOnDisk(true).bitmapConfig(Bitmap.Config.RGB_565).build();
ImageLoader.getInstance().displayImage(url,iv,options);
- 这个方法不需要手动setImageBtimap,直接将ImageView作为一个参数传入到displayImage,即displayImage(url,iv,options)
4、加载其他来源的图片
加载其他来源的图片和上述同理,把String url = “http://172.31.5.59:8080/IMG_20120825_103900.jpg“;中的地址换成对应的地址即可
Content provider来源的图片
- 图片地址即为存储的位置
#
String url = “content://image.png”;
- 图片地址即为存储的位置
- assets及drawable来源的图片
- 在图片来源位置包裹上Scheme作为图片的地址
- 例:String url = ImageDownloader.Scheme.ASSETS.wrap(“image.png”);
- String url = ImageDownloader.Scheme.DRAWABLE.wrap(“R.drawable.image”);
- 或者
- String url = “assets://image.png”;
- String url = “drawable://” + R.drawable.image;
- 在图片来源位置包裹上Scheme作为图片的地址
- SDcard来源的图片
- file:// + 图片路径
- String url = “file:///storage/emulated/0/image.png”;
- 或者
- String str = “/storage/emulated/0/image.png”;
- String url = ImageDownloader.Scheme.FILE.wrap(str);
- file:// + 图片路径
5、listview及gridview滑动时暂停加载图片
//2个true参数:第一个true表示滑动的时候暂停加载图片;第二个true表示快速滑动的时候暂停加载图片;
//最后一个参数默认为null(可选项),你也可以通过它来自定义图片加载的行为
listView.setOnScrollListener(new PauseOnScrollListener(ImageLoader.getInstance(),true,true,new AbsListView.OnScrollListener()));
- 注:需要添加加载中及加载失败图片以示是否加载
6、显示图片加载进度
你需要在布局文件中配置你的ProgressBar
//第一个参数:要加载的图片地址;第二个参数:要显示图片的控件;第三个参数:DisplayImageOptions对象;
//第四个参数:图片加载监听器,要重写四个方法以控制控件的显示;第五个参数:图片加载进度监听器
ImageLoader.getInstance().displayImage(urlString, holder.iv, initDisplayImageOptions(), new ImageLoadingListener(), new ImageLoadingProgressListener() {
@Override
public void onProgressUpdate(String imageUri, View view, int current, int total) {
//给你自己布局文件中的ProgressBar控件设置当前进度current和总长度total
//注意:DisplayImageOptions必须配置了硬盘缓存cacheOndisk(true),且加载的图片在sd卡中没有缓存,此方法才能被执行
}
});
private DisplayImageOptions initDisplayImageOptions() {
DisplayImageOptions disOptions = new DisplayImageOptions.Builder()
.showImageOnLoading(R.mipmap.ic_launcher)//设置图片加载中显示的图片
.showImageForEmptyUri(R.mipmap.ic_launcher)//设置Uri为null或”“时显示的图片
.showImageOnFail(R.mipmap.ic_launcher)//设置加载/解码失败时显示的图片
.cacheOnDisk(true)//设置缓冲到硬盘
.cacheInMemory(true)//设置缓冲到内存
.bitmapConfig(Bitmap.Config.RGB_565)
.displayer(new RoundedBitmapDisplayer(50))//如果使用loadImage,会报异常
.build();-
return disOptions;
}
7、清理缓存
#
ImageLoader.getInstance().clearMemoryCache();//清理内存中的缓存
ImageLoader.getInstance().clearDiskCache();//清理SD卡中的缓存
8、图片缓存—源码分析
内存缓存
- 一、什么是强引用和什么是弱引用?
- 强引用是指创建一个对象并把这个对象赋给一个引用变量, 强引用有引用变量指向时永远不会被垃圾回收。即使内存不足(OOM 内存溢出异常)的时候也不被垃圾回收器回收,例如:实例化对象 new对象 就是强引用
- 弱引用通过weakReference类来实现,它具有很强的不确定性,如果垃圾回收器扫描到有着WeakReference的对象,就会将其回收释放内存
#
public static String generateKey(String imageUri, ImageSize targetSize) {
return imageUri + “_” + targetSize.getWidth() + “x” + targetSize.getHeight();
}
扩展:虚引用及软引用
- 虚引用:“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收
- 软引用:如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存
- 弱引用和软引用的区别:在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。而,内存控件足够的情况下,软引用是不会被回收的
补充:配置configuration:在之前创建的MyApplication里面配置如下:
#
ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(this)
.memoryCache(new WeakMemoryCache())
.build();二、Universal-Image-Loader的内存缓存策略
- 1.只使用强引用缓存
- LruMemoryCache(这个类就是这个开源框架默认的内存缓存类,缓存的是bitmap的强引用)
- 1)这个方法使用的是bitmap的强引用,维护的是一个LinkedHashMap(Hashmap的一个子类,通过键值对来存放图片),即我们是通过操作这个集合里面的图片来实现图片的缓存
- 2)设定一个图片缓存的最大值,然后缓存图片的时候,计算每个图片的大小(bytes值)并记录当前缓存的总的大小,如果正在缓存的图片在之前缓存过,则把之前缓存的移除
- 3)当当前缓存的图片总量小于我们设定的缓存最大值,则不做处理。如果当前缓存的总量大于了我们设定的最大值,则删除LinkedHashMap(双重链表,迭代顺序)集合中的第一个元素
- 注:强引用是在内存不足即使报OOM内存溢出异常也不被垃圾回收,这里的内存和我们设定的缓存最大值不一样;例:我们设定的最大值超过了我们的内存上限,那么在缓存到达我们设定的最大值之前,已经超过了内存的上限了,这时候是没有达到设定的缓存最大值,所以,这时候缓存是不会被回收
- LruMemoryCache(这个类就是这个开源框架默认的内存缓存类,缓存的是bitmap的强引用)
2.使用强引用和弱引用结合的缓存
UsingFreqLimitedMemoryCache(如果缓存的图片总量超过限定值,先删除使用频率最小的缓存图片)
- 1)设定缓存的总量大小
- 2)在清除的方法中定义了一个三方变量,在删除的方法中先使用Iterator遍历进行判断,如果遍历结果为空,则不执行,不为空则执行删除方法
3)删除的时候,逐个进行比较,用前一个的使用频率和后一个的使用频率进行比较将使用频率小的key值赋给三方变量,然后再和下一个进行比较,如果下一个的使用频率比当前的小,则再次执行交换。最后得到使用频率最小的,调用删除remove得到的使用频率最小的缓存图片
#
public Bitmap get(String key) {
Bitmap value = super.get(key);
if(value != null) {
Integer usageCount = (Integer)this.usingCounts.get(value);
if(usageCount != null) {
this.usingCounts.put(value, Integer.valueOf(usageCount.intValue() + 1));
}
}return value;
}- 这是我找到的一段关于频率的代码,从中可以看出:这个缓存策略是将 保存的图片作为key,频率作为value保存到一个hashmap集合中,在使用的时候这个值+1,从而判断出使用的频率
- LRULimitedMemoryCache(这个也是使用的lru算法,和LruMemoryCache不同的是,他缓存的是bitmap的弱引用)
- 这个和LruMemoryCache同理,不同的是:在内存不足的时候,即使没有达到设定的缓存最大值,也会回收缓存的图片
- FIFOLimitedMemoryCache(先进先出的缓存策略,当超过设定值,先删除最先加入缓存的bitmap
- 该方法是使用的LinkedList集合在保存缓存图片的,保存的使用调用的list.add()的方法将缓存加入到了LinkedList集合中,这样在缓存的时候就是从第“0”项开始保存,在缓存达到最大值的时候,设定死删除第“0”项,这样每次达到缓存上限都是删除的集合中的第一个元素即当前集合中最先保存的元素,从而达到了先进(最早保存进来)先出(最早保存的最先删除)的缓存策略
- LargestLimitedMemoryCache(当超过缓存限定值,先删除最大的bitmap对象)
- 和UsingFreqLimitedMemoryCache类似:定义三方变量,然后逐个比较,将每次比较的最大的值赋给三方变量, 最后根据三方变量的key值来删除缓存的图片
- LimitedAgeMemoryCache(当 bitmap加入缓存中的时间超过我们设定的值,将其删除)
- 1)缓存图片的时候,获得系统的当前时间,将缓存图片的时间也通过键值对保存在一个HashMap集合中,缓存图片的键key和保存的时间的键key是同样的键
#
public boolean put(String key, Bitmap value) {
boolean putSuccesfully = this.cache.put(key, value);
if(putSuccesfully) {
this.loadingDates.put(key, Long.valueOf(System.currentTimeMillis()));
}
return putSuccesfully;
} - 2)通过get键key获得缓存时的时间,再得到系统当前时间;然后通过系统时间和缓存时间的时间长与我们设定的缓存时间最大值进行比较,如果超过了我们设定的最大时间,通过该键key清除图片及缓存时间
- 1)缓存图片的时候,获得系统的当前时间,将缓存图片的时间也通过键值对保存在一个HashMap集合中,缓存图片的键key和保存的时间的键key是同样的键
- 3.只使用弱引用缓存
- WeakMemoryCache(这个类缓存bitmap的总大小没有限制,唯一不足的地方就是不稳定,缓存的图片容易被回收掉)
- 如同介绍的,不设定缓存最大值,但是在内存不足的时候,就会被回收器回收
- WeakMemoryCache(这个类缓存bitmap的总大小没有限制,唯一不足的地方就是不稳定,缓存的图片容易被回收掉)
- 1.只使用强引用缓存
硬盘缓存
- 1.LimitedAgeDiscCache(设定文件存活的最长时间,当超过这个值,就删除该文件)
- 2.UnlimitedDiscCache(这个缓存类没有任何的限制,继承于BaseDiskCache)
9、源码的简单解读
- 先来看一段代码:这个就是我们上面使用的配置显示的图片参数的代码
#
iv = (ImageView) findViewById(R.id.iv);
String url = “http://172.31.5.59:8080/a20.jpg“;
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.load)
.showImageOnFail(R.drawable.load_error)
.cacheInMemory(true)
.bitmapConfig(Bitmap.Config.RGB_565).build();
ImageLoader.getInstance().displayImage(url,iv,options);
- 然后我们点开displayImage查看我们上面用到的源码
#
public void displayImage(String uri, ImageView imageView, DisplayImageOptions options) {
this.displayImage(uri, (ImageAware)(new ImageViewAware(imageView)), options, (ImageLoadingListener)null, (ImageLoadingProgressListener)null);
}
* 1)可以看出这里第二个参数ImageView被转化成了ImageViewAware对象,这一步是对内存的一个优化。ImageViewAware主要是将ImageView进行了一个包装,把ImageView的强引用编程了一个弱引用,这样在内存不足的时候就可以被回收掉;还有一点就是可以获取ImageView的宽和高,这样就可以根据它的宽高,对过大的图片进行剪裁,从而减少内存的使用
* 2)这么我们还看到了2个传null的监听,第一个监听: 执行的是 开始onLoadingStarted、失败onLoadingFailed、完成onLoadingComplete以及取消onLoadingCancelled的操作; 第二个监听:是对图片加载进度的一个监听
- 再来看一段displayImage具体的方法
#
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) 下面对于这个方法 分开来说明
#
this.checkConfiguration();
if(imageAware == null) {
throw new IllegalArgumentException(“Wrong arguments were passed to displayImage() method (ImageView reference must not be null)”);
} else {
if(listener == null) {
listener = this.defaultListener;
}if(options == null) { options = this.configuration.defaultDisplayImageOptions; } if(TextUtils.isEmpty(uri)) { this.engine.cancelDisplayTaskFor(imageAware); listener.onLoadingStarted(uri, imageAware.getWrappedView()); if(options.shouldShowImageForEmptyUri()) { imageAware.setImageDrawable(options.getImageForEmptyUri(this.configuration.resources)); } else { imageAware.setImageDrawable((Drawable)null); } listener.onLoadingComplete(uri, imageAware.getWrappedView(), (Bitmap)null); }
这段主要是对于情况为空进行的操作,第一句是判断configuration是否初始化,然后对于 imageAware、listener、options以及url为空的时候进行的操作:imageAware为空 抛出异常,listener、options空就使用默认设置,对于url为空的操作: engine是 ImageLoaderEngine的对象,然后点开ImageLoaderEngine会看到里面存在着一个HashMap,这个HashMap是用来记录正在加载的任务,加载图片的时候会将ImageView的id、图片的url和尺寸加入到这个HashMap中,加载完成的时候再将这个移除。当url为空的时候,将DisplayImageOptions中设置好的imageResForEmptyUri图片设置传给ImageView,然后再传给ImageLoadingListner监听的完成方法告诉程序,这次加载完成了
接着上面的代码
#
else {
if(targetSize == null) {
targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, this.configuration.getMaxImageSize());
}
//上面这段是将ImageView的宽高封装成一个尺寸对象(ImageSize targetSize)
String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
this.engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);
listener.onLoadingStarted(uri, imageAware.getWrappedView());
//下面是从内存缓存中获取Bitmap对象
Bitmap bmp = this.configuration.memoryCache.get(memoryCacheKey);
ImageLoadingInfo imageLoadingInfo;
if(bmp != null && !bmp.isRecycled()) {
L.d("Load image from memory cache [%s]", new Object[]{memoryCacheKey});
//判断图片处理是否设置了,如果设置了就进入里面的逻辑
if(options.shouldPostProcess()) {
imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, this.engine.getLockForUri(uri));
ProcessAndDisplayImageTask displayTask1 = new ProcessAndDisplayImageTask(this.engine, bmp, imageLoadingInfo, defineHandler(options));
if(options.isSyncLoading()) {
displayTask1.run();
} else {
this.engine.submit(displayTask1);
}
} else {
//将Bitmap设置到ImageView上
options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
}
}
* 接着往下看:
#
else {
if(options.shouldShowImageOnLoading()) {
imageAware.setImageDrawable(options.getImageOnLoading(this.configuration.resources));
} else if(options.isResetViewBeforeLoading()) {
imageAware.setImageDrawable((Drawable)null);
}
imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, this.engine.getLockForUri(uri));
LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(this.engine, imageLoadingInfo, defineHandler(options));
if(options.isSyncLoading()) {
displayTask.run();
} else {
this.engine.submit(displayTask);
}
}
* 上面这段代码中Bitmap不是在内存缓存中, 是从文件或者网络获取bitmap对象,实例化 LoadAndDisplayImageTask对象,LoadAndDisplayImageTask的实现Runnable接口的一个类;如果配置了同步加载isSyncLoading,则直接执行这个类中的run()方法。如果没有配置,则将LoadAndDisplayImageTask提交给线程池对象。
- 看一下 LoadAndDisplayImageTask的run()方法:
#
public void run() {
if(!this.waitIfPaused()) {
if(!this.delayIfNeed()) { - run()方法中前有2个判断,如果这2个方法有一个返回的是true,那么run方法就不会往下执行,直接返回。然后我们来看看着2个方法
#
//这个方法主要是为了使gridview和listview在滑动的时候不去加载图片
private boolean waitIfPaused() {
AtomicBoolean pause = this.engine.getPause();
if(pause.get()) {
synchronized(this.engine.getPauseLock()) {
if(pause.get()) {
L.d(“ImageLoader is paused. Waiting… [%s]”, new Object[]{this.memoryCacheKey});
try {
this.engine.getPauseLock().wait();
} catch (InterruptedException var5) {
L.e("Task was interrupted [%s]", new Object[]{this.memoryCacheKey});
return true;
}
L.d(".. Resume loading [%s]", new Object[]{this.memoryCacheKey});
}
}
}
return this.isTaskNotActual();
}
* 上面返回了一个isTaskNotActual(),那么我们来看看这个方法
#
//返回的是ImageView是否被回收和重用
private boolean isTaskNotActual() {
return this.isViewCollected() || this.isViewReused();
}
* 判断ImageView是否被回收和重用,如果被回收或者重用返回的是true,那么run()方法就不往下执行,直接返回。
* 为什么要判断是否重用呢?
* 答:我们都知道ListView和GridView需要重复使用到item对象,如果我们加载ListView或者GridView 的时候,还没有全部加载完成,我们就开始滑动屏幕了,着时候isViewReused()方法就不会让item去加载那些被我们快速的滑动忽略的图片,二直接加载我们现在屏幕停留的界面的图片
现在,我们想象一个场景:在一个ListView中,某个条目正在从网络获取图片,还没有获取完成,这时候,我们滑动屏幕,滑出了这个界面,然后又滑动回这个界面,我们闲着蛋疼 短时间来回划好多次,那么这个时候就会多次的向网络获取这个图片
- 这个时候呢,我们就需要一把锁,锁定图片的url,这样所有重复的url全部给锁定等待,然后图片加载完成后打开这个锁,这些具有相同url的对象在网络获取前 先在内存缓存中获取一遍,如果内存缓存中已经存在这个图片,就不去执行在网络获取的请求。这样就可以避免了 来回滑动的时候 多次向网络请求获取同一个图片了;(代码 我就不贴了,有兴趣的 可以去点开源码看看)
我们来看看上面是怎样先从文件缓存中获取有没有Bitmap对象的
#
//从硬盘获取图片文件
File imageFile = configuration.diskCache.get(uri);
//判断缓存中是否存在该文件
if (imageFile != null && imageFile.exists()) {
L.d(LOG_LOAD_IMAGE_FROM_DISK_CACHE, memoryCacheKey);
loadedFrom = LoadedFrom.DISC_CACHE;checkTaskNotActual(); //如果缓存中存在,调用decodeImage方法解码获取该图片 bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));
}
如果缓存中没有Bitmap:
#
//判断文件缓存中是否存在Bitmap对象
if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
L.d(LOG_LOAD_IMAGE_FROM_NETWORK, memoryCacheKey);
loadedFrom = LoadedFrom.NETWORK;String imageUriForDecoding = uri; //判断DisplayImageOptions中是否配置了硬盘缓存isCacheOnDisk //tryCacheImageOnDisk()方法为去服务器获取图片并保存到硬盘中 if (options.isCacheOnDisk() && tryCacheImageOnDisk()) { imageFile = configuration.diskCache.get(uri); //获取网络上的图片不为空,保存到本地 if (imageFile != null) { imageUriForDecoding = Scheme.FILE.wrap(imageFile.getAbsolutePath()); } } checkTaskNotActual(); bitmap = decodeImage(imageUriForDecoding); if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) { fireFailEvent(FailType.DECODING_ERROR, null); }
}
- 对于tryCacheImageOnDisk()的解读
#
private boolean tryCacheImageOnDisk() throws TaskCancelledException {
L.d(LOG_CACHE_IMAGE_ON_DISK, memoryCacheKey);
boolean loaded;
try {
loaded = downloadImage();
if (loaded) {
//设置缓存的图片的宽高,这里是原图大小
int width = configuration.maxImageWidthForDiskCache;
int height = configuration.maxImageHeightForDiskCache;if (width > 0 || height > 0) { L.d(LOG_RESIZE_CACHED_IMAGE_FILE, memoryCacheKey); resizeAndSaveImage(width, height); // TODO : process boolean result } }
} catch (IOException e) {
L.e(e);
loaded = false;
}
return loaded;
}
//下载图片并保存到文件缓存中的方法
private boolean downloadImage() throws IOException {
InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader());
return configuration.diskCache.save(uri, is, this);
}
//注:设置保存图片的尺寸在MyApplication中初始化的时候配置就可以了看下一段代码
#
if (bmp == null) return;checkTaskNotActual(); checkTaskInterrupted(); //对图片进行处理 if (options.shouldPreProcess()) { L.d(LOG_PREPROCESS_IMAGE, memoryCacheKey); bmp = options.getPreProcessor().process(bmp); if (bmp == null) { L.e(ERROR_PRE_PROCESSOR_NULL, memoryCacheKey); } } //将图片缓存到内存缓存中 if (bmp != null && options.isCacheInMemory()) { L.d(LOG_CACHE_IMAGE_IN_MEMORY, memoryCacheKey); configuration.memoryCache.put(memoryCacheKey, bmp); }
- 显示任务
DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
runTask(displayBitmapTask, syncLoading, handler, engine);
* 上面显示任务的run()方法
#
@Override
public void run() {
if (imageAware.isCollected()) {
L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);
listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
} else if (isViewWasReused()) {
L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey);
listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
//上面这段代码,如果ImageView被回收或者重用,再调用ImageLoadingListener监听接口的注销方法不加载图片,
//如果没有回收或重用,则调用display显示图片
} else {
L.d(LOG_DISPLAY_IMAGE_IN_IMAGEAWARE, loadedFrom, memoryCacheKey);
displayer.display(bitmap, imageAware, loadedFrom);
engine.cancelDisplayTaskFor(imageAware);
listener.onLoadingComplete(imageUri, imageAware.getWrappedView(), bitmap);
}
}