LruCache & DiskLruCache
在用户界面(UI)加载一张图片时很简单,然而,如果你需要加载多张较大的图像,事情就会变得更加复杂,。在许多情况下(如与像的ListView GridView或ViewPager的组件),屏幕上的图片的总数伴随屏幕上滚动的骤然增加,且基本上是无限的。为使内存使用保持在稳定范围内,这些组件会在子view在屏幕中消失后,对其进行资源回收,垃圾回收机制会释放掉已加载的图片内存空间,所以建议你不要保持图片的常引用,这样做很好,为了保证页面的流畅性和响应速度,你可能不愿意在页面返回时频繁处理加载过图片。通过内存、磁盘的缓存就可以帮助你快速加载已加载的图片。
今天与大家分享一下图片的缓存技术,利用它可以提高UI的流畅性、响应速度,给用户好的体验。
如何在内存中做缓存?
通过内存缓存可以快速加载缓存图片,但会消耗应用的内存空间。LruCache类(通过兼容包可以支持到sdk4)很适合做图片缓存,它通过LinkedHashMap保持图片的强引用方式存储图片,当缓存空间超过设置定的限值时会释放掉早期的缓存。
注:在过去,常用的内存缓存实现是通过SoftReference或WeakReference,但不建议这样做。从Android2.3(API等级9)垃圾收集器开始更积极收集软/弱引用,这使得它们相当无效。此外,在Android 3.0(API等级11)之前,存储在native内存中的可见的bitmap不会被释放,可能会导致应用程序暂时地超过其内存限制并崩溃。
为了给LruCache设置合适的大小,需要考虑以下几点因素:
你的应用中空闲内存是多大?
你要在屏幕中一次显示多少图片? 你准备多少张图片用于显示?
设备的屏幕大小与density 是多少?超高屏幕density的设备(xhdpi)像Galaxy Nexus 比 Nexus S (hdpi)这样的设备在缓存相同的图片时需要更大的Cache空间。
图片的大小和属性及其需要占用多少内存空间?
图片的访问频率是多少? 是否比其他的图片使用的频率高?如果这样你可能需要考虑将图片长期存放在内存中或者针对不同类型的图片使用不同的缓存策略。
如何平衡质量与数量,有事你可能会存储一些常用的低质量的图片用户显示,然后通过异步线程加载高质量的图片。
图片缓存方案没有固定的模式使用所有的的应用,你需要根据应用的具体应用场景进行分析,选择合适的方案来做,缓存太小不能发挥缓存的优势,太大可能占用过多的内存,降低应用性能,或者发生内存溢出异常,
下面是一个使用LruCache的例子:
private LruCache mMemoryCache; @Override
protected void onCreate(Bundle savedInstanceState) {
...
// Get memory class of this device, exceeding this amount will throw an
// OutOfMemory exception.
final int memClass = ((ActivityManager) context.getSystemService(
Context.ACTIVITY_SERVICE)).getMemoryClass(); // Use 1/8th of the available memory for this memory cache.
final int cacheSize = 1024 * 1024 * memClass / 8; mMemoryCache = new LruCache(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in bytes rather than number of items.
return bitmap.getByteCount();
}
};
...
} public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
} public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
注意:在这个例子中,应用八分子一的内存分配给图片缓存,在普通/hdpi设备中大约为4MB(32/8)。GirdView全屏时在800x480分辨率的设备中需要1.5M图片空间(800*480*4 bytes),这样就可以在内存中缓存2.5屏的图片。
运用LruCache向ImageView添加图片时首先先检查图片是否存在,如果在直接更行ImageView,否则通过后台线程加载图片:
public void loadBitmap(int resId, ImageView imageView) {
final String imageKey = String.valueOf(resId);
final Bitmap bitmap = getBitmapFromMemCache(imageKey);
if (bitmap != null) {
mImageView.setImageBitmap(bitmap);
} else {
mImageView.setImageResource(R.drawable.image_placeholder);
BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
task.execute(resId);
}
}
BitmapWorkerTask需要将将加载的图片添加到缓存中:
class BitmapWorkerTask extends AsyncTask {
...
// Decode image in background.
@Override
protected Bitmap doInBackground(Integer... params) {
final Bitmap bitmap = decodeSampledBitmapFromResource(
getResources(), params[0], 100, 100));
addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
return bitmap;
}
...
}
如何使用磁盘缓存?
内存缓存对访问最近使用的图片时很高效,但是你不能保证它一直会在缓存中。像GirdView这样大数据量的组件很容易充满内存缓存。你的应用可能会被“来电”打断,在后台时可能会被杀掉,内存缓存就会失效,一旦用户重新回到应用中时,你需要重新处理每个图片。
在这种情况下我们可以运用磁盘缓存存储已处理的图片,当图片不再内存中时,减少重新加载的时间,当然从磁盘加载图片时要比内存中慢,需要在后台线程中做,因为磁盘的读取时间是未知的。
注意:如果你经常访问图片,ContentProvider应该是存储图片的好地方,如:Gallery图片管理应用。
下面是一个简单的DiskLruCache实现。然而推荐的实现DiskLruCache方案请参考Android4.0中(libcore/luni/src/main/java/libcore/io/DiskLruCache.java)源码。本文使用的是之前版本中的简单实现(Quick Search中是另外的实现).
显示是简单实现DiskLruCache更新后的例子:
private DiskLruCache mDiskCache;
private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
private static final String DISK_CACHE_SUBDIR = "thumbnails"; @Override
protected void onCreate(Bundle savedInstanceState) {
...
// Initialize memory cache
...
File cacheDir = getCacheDir(this, DISK_CACHE_SUBDIR);
mDiskCache = DiskLruCache.openCache(this, cacheDir, DISK_CACHE_SIZE);
...
} class BitmapWorkerTask extends AsyncTask {
...
// Decode image in background.
@Override
protected Bitmap doInBackground(Integer... params) {
final String imageKey = String.valueOf(params[0]); // Check disk cache in background thread
Bitmap bitmap = getBitmapFromDiskCache(imageKey); if (bitmap == null) { // Not found in disk cache
// Process as normal
final Bitmap bitmap = decodeSampledBitmapFromResource(
getResources(), params[0], 100, 100));
} // Add final bitmap to caches
addBitmapToCache(String.valueOf(imageKey, bitmap); return bitmap;
}
...
} public void addBitmapToCache(String key, Bitmap bitmap) {
// Add to memory cache as before
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
} // Also add to disk cache
if (!mDiskCache.containsKey(key)) {
mDiskCache.put(key, bitmap);
}
} public Bitmap getBitmapFromDiskCache(String key) {
return mDiskCache.get(key);
} // Creates a unique subdirectory of the designated app cache directory. Tries to use external
// but if not mounted, falls back on internal storage.
public static File getCacheDir(Context context, String uniqueName) {
// Check if media is mounted or storage is built-in, if so, try and use external cache dir
// otherwise use internal cache dir
final String cachePath = Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
|| !Environment.isExternalStorageRemovable() ?
context.getExternalCacheDir().getPath() : context.getCacheDir().getPath(); return new File(cachePath + File.separator + uniqueName);
}
内存缓存检查在UI线程中做,磁盘缓存的检查在后台线程中。硬盘操作不应在UI线程中。图片处理完成后应将其加入正在使用的内存、磁盘缓存中。
LruCache & DiskLruCache的更多相关文章
- 安卓开发笔记——关于照片墙的实现(完美缓存策略LruCache+DiskLruCache)
这几天一直研究在安卓开发中图片应该如何处理,在网上翻了好多资料,这里做点小总结,如果朋友们有更好的解决方案,可以留言一起交流下. 内存缓存技术 在我们开发程序中要在界面上加载一张图片是件非常容易的事情 ...
- LruCache DiskLruCache 缓存 简介 案例 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- 打造完美的ImageLoader——LruCache+DiskLruCache
做android应用少不了要和网络打交道,在我刚开始学android的时候总是处理不好网络图片的加载,尤其是图片乱跳的问题,后来发现了各种缓存图片的方法:本地缓存.软引用.LruCache.... 我 ...
- Android Volley框架的使用(四)图片的三级缓存策略(内存LruCache+磁盘DiskLruCache+网络Volley)
在开发安卓应用中避免不了要使用到网络图片,获取网络图片很简单,但是需要付出一定的代价——流量.对于少数的图片而言问题不大,但如果手机应用中包含大量的图片,这势必会耗费用户的一定流量,如果我们不加以处理 ...
- DiskLruCache硬盘缓存技术详解
上次讲了使用内存缓存LruCache去加载很多图片而不造成OOM,而这种缓存的特点是在应用程序运行时管理内存中的资源(图片)的存储和释放,如果LruCache中有一张图片被释放了,再次加载该图片时需要 ...
- 让App中加入LruCache缓存,轻松解决图片过多造成的OOM
上次有过电话面试中问到Android中的缓存策略,当时模糊不清的回答,现在好好理一下吧. Android中一般情况下采取的缓存策略是使用二级缓存,即内存缓存+硬盘缓存->LruCache+Dis ...
- 让App中增加LruCache缓存,轻松解决图片过多造成的OOM
上次有过电话面试中问到Android中的缓存策略,当时模糊不清的回答,如今好好理一下吧. Android中普通情况下採取的缓存策略是使用二级缓存.即内存缓存+硬盘缓存->LruCache+Dis ...
- 【第五篇】Volley代码修改之图片二级缓存以及相关源码阅读(重写ImageLoader.ImageCache)
前面http://www.cnblogs.com/androidsuperman/p/8a157b18ede85caa61ca5bc04bba43d0.html 有讲到使用LRU来处理缓存的,但是只是 ...
- ImageLoader 笔记
BitmapFactory 我们不能够通过构造函数创建Bitmap对象.如果需要将图片转成Bitmap对象加载到内存中,就需要使用BitmapFactory类.BitmapFactory跟据图片数据源 ...
随机推荐
- java 图片合成文字或者awt包下的对话框引入自定义字体库
成就卡图片字体问题:1.下载 xxx.ttf 文件2.mkdir /usr/local/jdk1.6.0_11/jre/lib/fonts/fallback. 在jre/lib/fonts 下 ...
- POJ 4118 开餐馆
Description 北大信息学院的同学小明毕业之后打算创业开餐馆.现在共有n 个地点可供选择.小明打算从中选择合适的位置开设一些餐馆.这 n 个地点排列在同一条直线上.我们用一个整数序列m1, m ...
- .NET如何进行数据库连接
在.net中连接sql server数据库,需引入System.Data.SqlClient命名空间: 1,最简单的连接方式: SqlConnection conn = new SqlConnecti ...
- JSP菜鸟之困
我一直想把java一套系统学好... 之前寒假学了android......feel good 大四又把jsp补习了一边.....85 但是苦于没有做过实例..... 暑假学PS之间想恶补一下jsp. ...
- ServletContext作用功能详解
ServletContext,是一个全局的储存信息的空间,服务器开始,其就存在,服务器关闭,其才释放.request,一个用户可有多个:session,一个用户一个:而servletContext,所 ...
- 一个jboss启动shell脚本
脚本1: #!/bin/sh # paulo@evencom.com.br #JBOSS_HOME JBOSS_HOME="/opt/app/jboss-eap-6.3" JAVA ...
- [vijos1246]文科生的悲哀(二)
[vijos1246]文科生的悲哀(二) 试题描述 化学不及格的Matrix67无奈选择了文科.他必须硬着头皮艰难地进行着文科的学习. 这学期的政治.历史和地理课本各有n章.每一科的教学必须按章节从前 ...
- IPython的常见用法
IPython :交互式的Python命令行 安装: pip install ipython 使用(命令行中启动): ipython # 与Python解释器的使用方法一致 IPython高级功能: ...
- android GET 请求在5.0版本的取不到数据,报IO异常兼容问题解决
使用lib类库xUtils-2.6.10.jar作为数据请求的框架,在android的5.0版会有兼容问题,取不到GET请求的数据. 但是POST没有问题,难取到数据. public static R ...
- 【2018 Multi-University Training Contest 5】
01: 02:https://www.cnblogs.com/myx12345/p/9436953.html 03: 04: 05:https://www.cnblogs.com/myx12345/p ...