Android中的缓存

网友投稿 930 2022-08-23

Android中的缓存

Android中的缓存

为什么会用到缓存呢?主要是流量耗不起啊,国内的公共场所的WiFi的普及率不高,因此必须考虑流量的问题,说白了,就是用户体验啊,每次都网络请求,消耗资源不说,网速不好的情况下还会有网络延时,用户体验不好。

Android中的缓存,从方式上来说,一般有网络缓存,磁盘缓存即SD卡缓存,内存缓存。网络缓存需要服务端的配合,用于加快网络请求的响应速度。磁盘缓存一般用DiskLruCache,当然也可以用SqlLite数据库,以及sharedpreference等作持久化处理。这里主要说下两种常用的缓存方法,LruCache、DiskLruCache。前者用于内存缓存,后者用于设备缓存,一般两者结合起来效果更好。

其实缓存的实现并不难,每一中缓存都会有三个基本操作,添加、获取、删除。了解这些了,就会有思路了。

再说LruCache、DiskLruCache,可以看到,两者都有Lru,那么Lru是什么呢?这是目前常用的一种缓存算法:近期最少使用算法,核心思想很简单,就是当缓存满时,会优先删除那些近期最少使用的缓存。那么现在分别了解下这两种缓存吧。

LruCache

LruCache内部用到的是LinkedHashMap,LinkedHashMap与HashMap的不同住处在于LinkedHashMap 维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。也就说它的插入和访问是有顺序的。另外LruCache是线程安全的。至于使用的话就很简单了。

// 初始化 int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); int cacheSize = maxMemory / 8;

mMemoryCache = new LruCache(cacheSize) { @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes() * value.getHeight() / 1024;

}

};

总缓存大小一般会设置为当前进程可用内存的1/8,当然这个数是可以自己设置的,这个数是推荐的。sizeOf方法是为了计算缓存对象的大小。如果有必要也可以重写entryRemoved来完成某些资源回收工作。

再看缓存的添加与删除,

//添加缓存 mMemoryCache.put(key,bitmap); //获取缓存 mMemoryCache.get(key); //删除缓存 mMemoryCache.remove(key);

DiskLruCache

DiskLruCache用与磁盘缓存,被官方推荐使用。下面来看看它的使用。

自从用了Gradle后,引入项目方便多了,谁用谁知道。

compile 'com.jakewharton:disklrucache:2.0.2'

创建DiskLruCache:

DiskLruCache mDiskLruCache = null; try {

File cacheDir = getDiskCacheDir(context, "bitmap"); if (!cacheDir.exists()) {

cacheDir.mkdirs();

}

mDiskLruCache = DiskLruCache.open(cacheDir, 1, 1, 10 * 1024 * 1024);

} catch (IOException e) {

e.printStackTrace();

}

解释下DiskLruCache.open的参数,第一个表示存储的路径,第二个表示应用的版本号,注意这里当版本号发生改变时会清空之前所有的缓存文件,而在实际开发中这个性质用的不多,所以直接写1。第三个表示单个节点对应的数据的个数,设置为1就可以了,第四个表示缓存的总大小,当超出这个值时,会清除一些缓存保证总大小不大于这个设定的值。

添加缓存:

第一步,网络-图片(文件也是一样的步骤的)并通过outputStream写入到本地

private 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;

}

第二步,处理缓存的key,直接用url作为key值时最有快捷的方式,但是url里会有特殊字符,不符合Android的命名规范,最好的办法就是把url进行MD5摘要。

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();

}

第三步 创建DiskLruCache.Editor的实例,写入数据

String key = hashKeyForDisk(imageUrl);

DiskLruCache.Editor editor = mDiskLruCache.edit(key); if (editor != null) {

OutputStream outputStream = editor.newOutputStream(0); if (downloadUrlToStream(imageUrl, outputStream)) {

editor.commit();

} else {

editor.abort();

}

}

mDiskLruCache.flush();

editor.commit()方法用来提交写入操作,editor.abort()回退整个操作。

读取缓存:

Bitmap bitmap = null;

String key = hashKeyFormUrl(url);

DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key); if (snapShot != null) {

FileInputStream fileInputStream = (FileInputStream)snapShot.getInputStream(0);

FileDescriptor fileDescriptor = fileInputStream.getFD();

bitmap = mImageResizer.decodeSampledBitmapFromFileDescriptor(fileDescriptor,

reqWidth, reqHeight); if (bitmap != null) {

addBitmapToMemoryCache(key, bitmap);

}

}

public Bitmap decodeSampledBitmapFromFileDescriptor(FileDescriptor fd, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options();

options.inJustDecodeBounds = true;

BitmapFactory.decodeFileDescriptor(fd, null, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth,

reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeFileDescriptor(fd, null, options);

}

需要说明下的是为了避免加载图片时导致OOM,不建议直接加在Bitmap,通常我们会通过BitmapFactory.Options来加载一张缩放的图片,但是这中方法对于FileInputStream有问题,因为FileInputStream是有序的文件流,而两次的从的 decodeStream调用影响了文件流的位置属性,导致第二次decodeStream时得到的为null。为了解决这个问题,可以先得到对应的文件描述符,然后通过BitmapFactory.decodeFileDescriptor()来加载图片。

移除缓存:

mDiskLruCache.remove(key);

来自:http://jianshu.com/p/96a7865fdab4

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Makefile:589: recipe for target '.build_release/src/caffe/proto/caffe.pb.o' failed
下一篇://home/idc/anaconda3/lib/libpng16.so.16: undefined reference to `inflateValidate@ZLIB_1.2.9'
相关文章

 发表评论

暂时没有评论,来抢沙发吧~