《android开发艺术探索》笔记之Bitmap的加载和Cache

《Android开发艺术探索》笔记之Bitmap的加载和Cache<二>

缓存:

目前常用的一种缓存算法是LRU(Least Recently Used),即近期最少使用算法,核心思想是当缓存满时,会优先淘汰近期最少使用的缓存对象。采用LRU算法的缓存有两种:LruCache和DiskLruCache,LruCache用于实现内存缓存,DiskLruCache则是存储设备缓存。

LruCache:

算法原理:把最近使用的对象用强引用存储在LinkedHashMap中,并且把最近使用的对象在缓存值达到预设值之前从内存中移除

强引用:直接的对象引用

软引用:当一个对象只有软引用存在时,系统内存不足时此对象会被gc回收

弱引用:当一个对象只有弱引用存在时,此对象会随时被gc回收

下面是一个使用 LruCache 来缓存图片的例子:

private LruCache mMemoryCache;  
@Override
protected void onCreate(Bundle savedInstanceState) { 
    // 获取到可用内存的最大值,使用内存超出这个值会引起OutOfMemory异常。 
    // LruCache通过构造函数传入缓存值,以KB为单位。 
    int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); 
    // 使用最大可用内存值的1/8作为缓存的大小。 
    int cacheSize = maxMemory / 8; 
    mMemoryCache = new LruCache(cacheSize) { 
        @Override
        protected int sizeOf(String key, Bitmap bitmap) { 
            // 重写此方法来计算缓存对象的大小(注意单位,转换成KB,与总容量单位一致)
            return bitmap.getByteCount() / 1024; 
            //或者return bitmap.getRowBytes()*bitmap.getHeight()/1024;
        } 
    }; 
} 
    
public void addBitmapToMemoryCache(String key, Bitmap bitmap) { 
    if (getBitmapFromMemCache(key) == null) { 
        mMemoryCache.put(key, bitmap); 
    } 
} 
    
public Bitmap getBitmapFromMemCache(String key) { 
    return mMemoryCache.get(key); 
}

DiskLruCache:

首先强调一点:DiskLruCache是非Google官方编写,但获得官方文档的推荐。如果要使用它,得先下载这个类,然后在项目中新建一个libcore.io包,将DiskLruCache.java文件复制到这个包中即可。

DiskLruCache通过将缓存对象写入文件系统从而实现缓存的效果。

DiskLruCache的缓存位置,可以自由地进行设定,但是通常情况下多数应用程序都会将缓存的位置选择为/sdcard/Android/data//cache 这个路径。选择在这个位置有两点好处:第一,这是存储在SD卡上的,因此即使缓存再多的数据也不会对手机的内置存储空间有任何影响,只要SD卡空间足够就行。第二,这个路径被Android系统认定为应用程序的缓存路径,当程序被卸载的时候,这里的数据也会一起被清除掉,这样就不会出现删除程序之后手机上还有很多残留数据的问题。

创建一个DiskLruCache的实例:

public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
open()方法接收四个参数,第一个参数指定的是数据的缓存地址,上面已经介绍过;第二个参数指定当前应用程序的版本号,一般设为1.当版本号发生改变时DiskLruCache会清空之前所有缓存文件。第三个参数指定同一个key可以对应多少个缓存文件,基本都是传1;第四个参数指定最多可以缓存多少字节的数据,当缓存大小超过这个设定值后,DiskLruCache会清除一些缓存从而保证总大小不大于这个设定值。

一个非常标准的open()方法就可以这样写:

private static final long DISK_CACHE_SIZE=1024*1024*50;
DiskLruCache mDiskLruCache = null;
try {
  File cacheDir = getDiskCacheDir(context, "bitmap");
  if (!cacheDir.exists()) {
    cacheDir.mkdirs();
  }
  mDiskLruCache = DiskLruCache.open(cacheDir,1, 1, DISK_CACHE_SIZE);
} catch (IOException e) {
  e.printStackTrace();
}


private File getDiskCacheDir(Context context,String uniqueName){
    boolean externalStorageAvailable=Environment.getExternalStorageState().equals(Enviroment.MEDIE_MOUNTED);
    //判断SD卡是否正常挂载
    final String cachePath;
    if(externalStorageAvailable){
        cachePath=context.getExternalCacheDir().getPath();
        //获取到SDCard/Android/data//cache/目录
    }else{
        cachePath=context.getCacheDir().getPath();
        //获取/data/data//cache目录
    }    
    return new File(cachePath+File.separator+uniqueName);
}


DiskLruCache的缓存添加:

先将url的md5值作为key,如下:

public String hashKeyForDisk(String key) {
  String cacheKey;
  try {
    final MessageDigest mDigest = MessageDigest.getInstance("MD5");
    mDigest.update(key.getBytes());
    cacheKey = bytesToHexString(mDigest.digest());
  } catch (NoSuchAlgorithmException e) {
    cacheKey = String.valueOf(key.hashCode());
  }
  return cacheKey;
}

private String bytesToHexString(byte[] bytes) {
  StringBuilder sb = new StringBuilder();
  for (int i = 0; i < bytes.length; i++) {
    String hex = Integer.toHexString(0xFF & bytes[i]);
    if (hex.length() == 1) {
      sb.append('0');
    }
    sb.append(hex);
  }
  return sb.toString();
}
然后就可以获得Editor对象,写入文件系统中,如下:

public boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
  HttpURLConnection urlConnection = null;
  BufferedOutputStream out = null;
  BufferedInputStream in = null;
  try {
    final URL url = new URL(urlString);
    urlConnection = (HttpURLConnection) url.openConnection();
    in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
    out = new BufferedOutputStream(outputStream, 8 * 1024);
    int b;
    while ((b = in.read()) != -1) {
      out.write(b);
    }
    return true;
  } catch (final IOException e) {
    e.printStackTrace();
  } finally {
    if (urlConnection != null) {
      urlConnection.disconnect();
    }
    try {
      if (out != null) {
        out.close();
      }
      if (in != null) {
        in.close();
      }
    } catch (final IOException e) {
      e.printStackTrace();
    }
  }
  return false;
}
String key=hashKeyFormUrl(url);
DiskLruCache.Editor editor=mDiskLruCache.edit(key);
if (editor != null) {
    //由于前面在DiskLruCache的open方法中设置了一个节点,
    //只能有一个数据,所以下面参数为0
        OutputStream outputStream = editor.newOutputStream(0);
        if (downloadUrlToStream(imageUrl, outputStream)) {
          editor.commit();
        } else {
          editor.abort();
        }
      }
mDiskLruCache.flush();

DiskLruCache的缓存查找:

和添加类似,先将url转换成key,然后通过DiskLruCache的get方法得到一个Snapshot对象,接着通过Snapshot对象得到缓存的文件输入流,最后得到Bitmap对象。如下:

Bitmap bitmap=null;
String key = hashKeyFormUrl(url);
DiskLruCache.Snapshot snapshot=mDiskLruCache.get(key);
if (snapshot!=null) {
   InputStream inputStream=snapshot.getInputStream(0);
   bitmap=BitmapFactory.decodeStream(inputStream);
if(bitmap!=null){
   addBitmapToMemoryCache(key,bitmap);}				
}
藉此,LruCache和DiskLruCache都介绍完了。



你可能感兴趣的:(《android开发艺术探索》笔记之Bitmap的加载和Cache)