Bitmap的加载和缓存

本文详细介绍了Android中Bitmap的加载,如何避免内存溢出,通过BitmapFactory.Options使用采样率减小图片大小。同时讲解了缓存策略,包括内存缓存LruCache和磁盘缓存DiskLruCache的实现,以及Universal-Image-Loader库的使用,强调其多线程下载、内存和磁盘缓存、图片裁剪等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、加载
由于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();  // 清除本地缓存
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值