学习笔记 | Android开发艺术之Bitmap&Cache

一、Bitmap的高效加载

a.核心思想

采用BitmapFactory.Options按一定的采样率来加载缩小后的图片,将缩小后的图片在ImageView中显示,这样就会降低内存占用从而在一定程度上避免OOM,提高了Bitmap加载时的性能。

b. 工具类
BitmapFactory类提供了四类方法:decodeFile、decodeResource、decodeStream和decodeByteArray

  • decodeFile():从文件系统加载出一个Bitmap对象
  • decodeResource():从资源文件加载出一个Bitmap对象
  • decodeStream():从输入流加载出一个Bitmap对象
  • decodeByteArray():从字节数组加载出一个Bitmap对象

二、缓存策略

缓存策略主要包含缓存的添加、获取和删除这三类操作。
Q1:为什么要删除缓存呢?
因为内存缓存和存储设备的缓存大小有容量限制,当缓存容量满了,但是程序还需要向其添加缓存,就需要删除一些旧的缓存并添加新的缓存。

1. LRU缓存算法

核心思想:当缓存满时,会优先淘汰那些近期最少使用的缓存对象。
分类:LruCache和DiskLruCache

1.1 LruCache

实现原理:LruCache是一个泛型类,它内部采用一个LinkedHashMap强引用的方式存储外界的缓存对象,其提供了getput方法来完成缓存的获取和添加操作,当缓存满时,LruCache会移除较早使用的缓存对象,然后再添加新的缓存对象。

    注:几种引用的含义
        强引用:直接的对象引用,不会被gc回收;
        软引用:当系统内存不足时,对象会被gc回收;
        弱引用:随时会被gc回收。

使用方法:

  • 计算当前可用的内存大小;
  • 分配LruCache缓存容量;
  • 创建LruCache对象并传入最大缓存大小的参数、重写sizeOf()用于计算每个缓存对象的大小;
  • 通过put()、get()和remove()实现数据的添加、获取和删除。

实例:

 //初始化LruCache对象
public void initLruCache()
{
    //1.获取当前进程的可用内存,转换成KB单位
    int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
    //2.分配缓存的大小
    int maxSize = maxMemory / 8;
    //3.创建LruCache对象并重写sizeOf方法
    lruCache = new LruCache<String, Bitmap>(maxSize)
        {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                // TODO Auto-generated method stub
                return value.getWidth() * value.getHeight() / 1024;
            }
        };
}
//4.LruCache对数据的操作
public void fun()
{
    //添加数据
    lruCache.put("lizhuo", bm1);
    lruCache.put("sushe", bm2);
    lruCache.put("jiqian", bm3);
    //获取数据
    Bitmap b1 = (lruCache.get("lizhuo"));
    Bitmap b2 = (lruCache.get("sushe"));
    Bitmap b3 = (lruCache.get("jiqian"));
    //删除数据
    lruCache.remove("sushe");
}
1.2 DiskLruCache

介绍:
   DiskLruCache用于实现存储设备缓存,即磁盘缓存,它通过将缓存对象写入文件系统从而实现缓存的效果。

与LruCache区别:
   DiskLruCache非泛型类,不能添加类型,而是采用文件存储,存储和读取通过I/O流处理。

使用方法:

  • 计算分配DiskLruCache的容量;
  • 设置缓存目录;
  • 创建DiskLruCache对象,注意不能通过构造方法来创建, 而是提供open()方法;
  • 利用Editor、Snapshot和remove()实现数据的添加、获取和删除。
  • 调用flush()将数据写入磁盘。

(1)DiskLruCache的创建

public static DiskLruCache open(File directory,int appVersion,int valueCount,long maxSize)

参数解读
directory:磁盘缓存的存储路径。有两种目录:
SD 上的缓存目录:/sdcard/Android/data/package_name/cache 目录,当应用被卸载后会被删除。
其他目录:应用卸载后缓存数据还在。

appVersion:当前应用的版本号,一般设为1。
valueCount:单个节点所对应的数据的个数,一般设为1。
maxSize:缓存的总大小,超出这个设定值后DiskLruCache会清除一些缓存

典型的DiskLruCache的创建过程如下:

DiskLruCache mDiskLruCache = null;  
try {  
    File cacheDir = getDiskCacheDir(context, "bitmap");  
    if (!cacheDir.exists()) {  
    //若缓存地址的路径不存在就创建一个
        cacheDir.mkdirs();  
    }  
    mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);  
} catch (IOException e) {  
    e.printStackTrace();  
}  
//用于获取到缓存地址的路径
public File getDiskCacheDir(Context context, String uniqueName) {  
    String cachePath;  
    if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())|| !Environment.isExternalStorageRemovable()) {  
    //当SD卡存在或者SD卡不可被移除,获取路径 /sdcard/Android/data/<application package>/cache
        cachePath = context.getExternalCacheDir().getPath();  
    } else { 
    //反之,获取路径/data/data/<application package>/cache 
        cachePath = context.getCacheDir().getPath();  
    }  
    return new File(cachePath + File.separator + uniqueName);  
}  
//用于获取到当前应用程序的版本号
public int getAppVersion(Context context) {  
    try {  
        PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);  
        return info.versionCode;  
    } catch (NameNotFoundException e) {  
        e.printStackTrace();  
    }  
    return 1;  
}  

(2)添加缓存操作:通过Editor完成

  • 获取资源的key值,采用url的md5值作为key;
  • 通过DiskLruCache.edit() 获取对应key的Editor;
  • 通过Editor.newOutputStream(0)得到一个输出流;
  • 通过OutputStream写入数据;
  • Editor.commit()提交写操作,若发生异常,则调用Editor.abort()进行回退。

核心代码:

//1.返回url的MD5算法结果
String key = hashKeyFormUrl(url);
//2.获取Editor对象
Editor editor = mDiskLruCache.edit(key);
//3.创建输出流,其中常量DISK_CACHE_INDEX = 0
OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
//4.写入数据
outputStream.wirte(data);
//5.提交写操作
editor.commit();

(3)查找缓存操作:和缓存添加的过程类似

  • 获取资源的key值,采用url的md5值作为key;
  • 通过DiskLruCache.get()获取对应key的Snapshot对象;
  • 通过Snapshot.getInputStream(0)得到一个输入流(可向下转型为FileInputStream);
  • 通过InputStream读取数据。
    核心代码:
//1.返回url的MD5算法结果
String key = hashKeyFormUrl(url);
//2.获取Snapshot对象
Snapshot snapshot = mDiskLruCache.get(key);
//3.创建输入流,其中常量DISK_CACHE_INDEX = 0
InputStream inputStream = snapshot.getInputStream(DISK_CACHE_INDEX);
//4.读出数据
int data = inputStream.read();

补充
Q:FileInputStream是一种有序的文件流,调用两次 BitmapFactory.decodeStream()会影响文件流的位置属性,导致第二次解析结果为空。
A:通过文件流得到其对应的文件描述符,再调用 BitmapFactory.decodeFileDescriptor()来加载一张缩放后的图片。

2. ImageLoader的实现

a.介绍: ImageLoader内部封装了Bitmap的高效加载、LruCache和DiskLruCache。

b.应具备功能:

  • 同步加载
  • 异步加载
  • 图片压缩
  • 内存缓存
  • 磁盘缓存
  • 网络拉取

c.使用场景:

  • 实现照片墙效果 ,此处实例
  • 优化 ListView/GridView卡顿现象,几点办法:
    (1) 不要在Adapter的getView()中执行耗时操作,比如直接加载图片。
    (2) 控制异步任务的执行频率,在列表滑动时停止加载图片,而列表停下时再加载图片,此处实例
    (3) 开启硬件加速,给Activity添加配置android:hardwareAccelerated=“true”。
      android中listview卡顿的终极原因解析
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值