一、加载
由于Bitmap的特殊性和Android对单个应用的内存限制,导致加载Bitmap时很容易出现内存溢出。
BitmapFactory提供了四类方法:decodeFile、decodeResource、decodeStream、decodeByteArray,分别支持从文件系统、资源、输入流、字节数组中加载一个Bitmap对象,其中decodeFile和decodeResource又间接调用了decodeStream。
通过BitmapFactory.Options按一定采样率来加载缩小后的图片:
1)将inJustDecodeBounds参数设为true并加载图片;
2)通过outWidth和outHeight取出图片的原始宽高;
3)结合目标View的大小计算出采样率inSampleSize;
4)将inJustDecodeBounds参数设为false重新加载图片;
二、缓存
三级缓存:内存–>存储设备–>网络
1)LruCache用于实现内存缓存
LruCache的创建
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8;
LruCache<String, Bitmap> mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
};
LruCache的获取、添加和删除
mMemoryCache.get(key);
mMemoryCache.put(key,bitmap);
mMemoryCache.remove(key);
2)DiskLruCache实现存储设备缓存
a.DiskLruCache的创建:
提供了open方法用于创建自身,
public static DiskLruCache open(File directory,int appVersion,int valueCount,long maxSize)
第一个参数表示磁盘缓存在文件系统中的存储路径,如果应用卸载后就删除缓存文件,选择SD卡上的缓存目录 /sdcard/Android/data/packageName/cache,如果希望保留缓存数据就选择SD卡的其它目录;
第二个参数表示应用版本号,一般设为1即可;
第三个参数表示单个节点所对应的数据个数,一般设为1即可;
第四个参数表示缓存的总大小;
b.DiskLruCache的缓存添加:
是通过Editor完成的,表示一个缓存对象的编辑对象,根据key通过edit()来获取Editor对象,如果这个缓存正在被编辑,会返回null,一般采用url的md5值作为key。
通过Editor可以得到一个文件输出流,从网络下载图片时,图片通过这个输出流写入到文件系统,经过Editor的commit()来提交写入操作,如果图片下载过程发生了异常,可以通过Editor的abort()来回退整个操作。
c.DiskLruCache的缓存查找:
根据key通过DiskLruCache的get方法得到一个Snapshot对象,通过Snapshot对象即可得到缓存的文件输入流。
三、Universal-Image-Loader的使用
Universal-Image-Loader的特征:
- 多线程下载图片,图片可以来源于网络,文件系统,项目文件夹assets中以及res/drawable中等
- 支持随意的配置ImageLoader,例如线程池,图片下载器,内存缓存策略,硬盘缓存策略,图片显示选项以及其他的一些配置
- 支持图片的内存缓存,文件系统缓存或者SD卡缓存
- 支持图片下载过程的监听
- 根据控件(ImageView)的大小对Bitmap进行裁剪,减少Bitmap占用过多的内存
- 较好的控制图片的加载过程,例如暂停图片加载,重新开始加载图片,一般使用在ListView,GridView中,滑动过程中暂停加载图片,停止滑动的时候去加载图片
- 提供在较慢的网络下对图片进行加载
ImageLoader的使用:
1)初始化配置
在onCreate方法中创建ImageLoader的配置参数ImageLoaderConfiguration,并初始化到ImageLoader当中。
可以使用createDefault()方法创建一个默认的ImageLoaderConfiguration对象:
ImageLoaderConfiguration configuration = ImageLoaderConfiguration.createDefault(this);
ImageLoader.getInstance().init(configuration);
也可以自己设置ImageLoaderConfiguration:
File cacheDir = StorageUtils.getCacheDirectory(this);//缓存文件夹路径
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this)
.memoryCacheExtraOptions(480, 800)
.diskCacheExtraOptions(480, 800, null)
.taskExecutor(DefaultConfigurationFactory.createExecutor(3, Thread.NORM_PRIORITY
- 1, QueueProcessingType.FIFO))
.taskExecutorForCachedImages(DefaultConfigurationFactory.createExecutor(3, Thread.NORM_PRIORITY
- 1, QueueProcessingType.FIFO))
.threadPoolSize(3)
.threadPriority(Thread.NORM_PRIORITY - 1)
.tasksProcessingOrder(QueueProcessingType.FIFO)
.denyCacheImageMultipleSizesInMemory()
.memoryCache(new LruMemoryCache(2 * 1024 * 1024))
.memoryCacheSize(2 * 1024 * 1024)
.memoryCacheSizePercentage(13)
.diskCache(new UnlimitedDiscCache(StorageUtils.getCacheDirectory(getApplicationContext())))
.diskCacheSize(50 * 1024 * 1024)
.diskCacheFileCount(100)
.diskCacheFileNameGenerator(new HashCodeFileNameGenerator())
.imageDownloader(new BaseImageDownloader(this))
.imageDecoder(new BaseImageDecoder(false))
.defaultDisplayImageOptions(DisplayImageOptions.createSimple())
.writeDebugLogs()
.build();
- memoryCacheExtraOptions()内存缓存的设置选项 (最大图片宽度,最大图片高度) 默认当前屏幕分辨率
- discCacheExtraOptions()硬盘缓存的设置选项 (最大图片宽度,最大图片高度,压缩格式,压缩质量,处理器)
- threadPoolSize() 设置线程池的大小. 每一个图片加载和显示的任务都是在一个个单独的线程中进行的,这些线程在从图片网络中被下载的时候就会进入线程池。因此,池的大小决定能同时运行的线程数。一个大的线程池能显著地拖慢UI的响应速度,例如,列表的滚动就会变慢.
- threadPriority()设置正在运行任务的所有线程在系统中的优先级(1到10);
- 调用denyCacheImageMultipleSizesInMemory()强制在内存中不能存储内容相同但大小不同的图像。由于完整大小的图片会存储在磁盘缓存中,后面当图片加载进入内存,他们就会缩小到ImageView的大小(图片要显示的尺寸),然而在某些情况下,相同的图像第一次显示在一个小的View中,然后又需要在一个大的View中显示。同时,两个不同大小的相同内容的图片就会被将被存储在内存中。这是默认的操作。denyCacheImageMultipleSizesInMemory()指令确保删除前一个加载的图像缓存的内存的大小
- taskExecutor()设置自定义加载和显示图片的线程池
- taskExecutorForCachedImages()设置自定义加载和显示内存缓存或者硬盘缓存图片的线程池
- tasksProcessingOrder()设置图片加载和显示队列处理的类型 默认为QueueProcessingType.FIFO
- memoryCache()设置内存缓存 默认为一个当前应用可用内存的1/8大小的LruMemoryCache
- memoryCacheSize()设置内存缓存的最大大小 默认为一个当前应用可用内存的1/8
- memoryCacheSizePercentage()设置内存缓存最大大小占当前应用可用内存的百分比 默认为一个当前应用可用内存的1/8
- diskCache()设置硬盘缓存
- discCacheSize()设置硬盘缓存的最大大小
- discCacheFileCount()设置硬盘缓存的文件的最多个数
- discCacheFileNameGenerator(new Md5FileNameGenerator())设置硬盘缓存文件名生成规范
- imageDownloader()设置图片下载器
- imageDecoder()设置图片解码器
- defaultDisplayImageOptions()设置默认的图片显示选项
- writeDebugLogs() 打印DebugLogs
- build()开始创建
2)使用
实例化ImageLoader
ImageLoader imageLoader = ImageLoader.getInstance();
初始化显示配置
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.mipmap.ic_launcher) //设置图片在下载期间显示的图片
.showImageForEmptyUri(R.mipmap.ic_launcher)//设置图片Uri为空或是错误的时候显示的图片
.showImageOnFail(R.mipmap.ic_launcher) //设置图片加载/解码过程中错误时候显示的图片
.cacheInMemory(true)//设置下载的图片是否缓存在内存中
.cacheOnDisc(true)//设置下载的图片是否缓存在SD卡中
.considerExifParams(true) //是否考虑JPEG图像EXIF参数(旋转,翻转)
.imageScaleType(ImageScaleType.EXACTLY_STRETCHED)//设置图片以如何的编码方式显示
.bitmapConfig(Bitmap.Config.RGB_565)//设置图片的解码类型
.decodingOptions(android.graphics.BitmapFactory.Options)//设置图片的解码配置
.delayBeforeLoading(int)//int delayInMillis设置的下载前的延迟时间
.preProcessor(BitmapProcessor)//设置图片加入缓存前,对bitmap进行设置
.resetViewBeforeLoading(true)//设置图片在下载前是否重置,复位
.displayer(new RoundedBitmapDisplayer(20))//是否设置为圆角,弧度为多少
.displayer(new FadeInBitmapDisplayer(100))//是否图片加载好后渐入的动画时间
.build();
3)显示图片
1)ImageLoader.getInstance().displayImage(uri, imageView);
2)ImageLoader.getInstance().displayImage(uri, imageView, options);
3)ImageLoader.getInstance().displayImage(uri, imageView, listener);
4)ImageLoader.getInstance().displayImage(uri, imageView, options, listener);
5)ImageLoader.getInstance().displayImage(uri, imageView, options, listener, progressListener);
- imageUrl 图片的URL地址
- imageView 显示图片的ImageView控件
- options DisplayImageOptions配置信息
- listener 图片下载情况的监听
- progressListener 图片下载进度的监听
Uri: - String imageUri = “http://site.com/image.png“; // 来源于 Web
- String imageUri = “file:///mnt/sdcard/image.png”; // 来源于 SD card
String imagePath = “/mnt/sdcard/image.png”;
String imageUrl = Scheme.FILE.wrap(imagePath); - String imageUri = “content://media/external/audio/albumart/13”; // 来源于 content provider
- String imageUri = “assets://image.png”; // 来源于 assets
String assetsUrl = Scheme.ASSETS.wrap(“image.png”);
String imageUri = “drawable://” + R.drawable.image; // 来源于drawables (only images, non-9patch)
String drawableUrl = Scheme.DRAWABLE.wrap(“R.drawable.image”);
监听器
imageLoader.displayImage(imageUrl, imageView, options, new ImageLoadingListener() {
@Override
public void onLoadingStarted() {
//开始加载的时候执行
}
@Override
public void onLoadingFailed(FailReason failReason) {
//加载失败的时候执行
}
@Override
public void onLoadingComplete(Bitmap loadedImage) {
//加载成功的时候执行
}
@Override
public void onLoadingCancelled() {
//加载取消的时候执行
},new ImageLoadingProgressListener() {
@Override
public void onProgressUpdate(String imageUri, View view, int current,int total) {
//在这里更新 ProgressBar的进度信息
}
});
4)清除缓存
ImageLoader.getInstance().clearMemoryCache(); // 清除内存缓存
ImageLoader.getInstance().clearDiskCache(); // 清除本地缓存