Android的图片缓存ImageCache(转)
为什么要做缓存?
在UI界面加载一张图片时很简单,然而如果需要加载多张较大的图像,事情就会变得更加复杂。在许多情况下(如ListView、GridView或ViewPager等的组件),屏幕上的图片的总数伴随屏幕的滚动会大大增加,且基本上是无限的。
如何在内存中做缓存?
通过内存缓存可以快速加载缓存图片,但会消耗应用的内存空间。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)
{
imageView.setImageBitmap(bitmap);
}
else
{
imageView.setImageResource(R.drawable.image_placeholder);
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
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线程中。图片处理完成后应将其加入正在使用的内存、磁盘缓存中。
如何处理配置的改变?
应用运行中配置改变时,如屏幕方向改变时为了应用新的配置Android会销毁重新运行当前的Activity,此时,为了给用户快速、平缓的用户体验你可能不想重新加载图片。
多亏你运行了缓存技术,缓存可以通过 setRetainInstance(true))传递给新的Activity,在Activity重启后,你可以通过附着的Fragment重新使用已存在的缓存,这样就可以快速加载到ImageView中了。
下面是一个当配置改变时用Fragment重用已有的缓存的例子:
private LruCache mMemoryCache; @Override
protected void onCreate(Bundle savedInstanceState)
{
...
RetainFragment mRetainFragment =
RetainFragment.findOrCreateRetainFragment(getFragmentManager());
mMemoryCache = RetainFragment.mRetainedCache;
if (mMemoryCache == null)
{
mMemoryCache = new LruCache(cacheSize)
{
... // Initialize cache here as usual
}
mRetainFragment.mRetainedCache = mMemoryCache;
}
...
} class RetainFragment extends Fragment
{
private static final String TAG = "RetainFragment";
public LruCache mRetainedCache; public RetainFragment() {} public static RetainFragment findOrCreateRetainFragment(FragmentManager fm)
{
RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);
if (fragment == null)
{
fragment = new RetainFragment();
}
return fragment;
} @Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
}
为了测试,在使用和未使用Fragment的情况下,强制旋转屏幕,你会发现从保留内存缓存加载图片时几乎没有滞后。
转自:链接
Android的图片缓存ImageCache(转)的更多相关文章
- 【MDCC 2015】开源选型之Android三大图片缓存原理、特性对比
摘要:这是快的打车移动端架构师.Android 开源项目源码解析codeKK发起人 吴更新(@Trinea)在MDCC上分享的内容,从总体设计和原理上对几个图片缓存进行对比,没用到它们的朋友也可以了解 ...
- 65.Android 三大图片缓存原理、特性对比 (转)
这是 Trinea 在 MDCC 上分享的内容(略微改动),也是源码解析第一期发布时介绍的源码解析后续会慢慢做的事. 从总体设计和原理上对几个图片缓存进行对比,没用到他们的朋友也可以了解他们在某些特性 ...
- Android四大图片缓存(Imageloader,Picasso,Glide,Fresco)原理、特性对比
四大图片缓存基本信息 Universal ImageLoader 是很早开源的图片缓存,在早期被很多应用使用. Picasso 是 Square 开源的项目,且他的主导者是 JakeWharton,所 ...
- Android 三大图片缓存原理、特性对比
这是我在 MDCC 上分享的内容(略微改动),也是源码解析第一期发布时介绍的源码解析后续会慢慢做的事. 从总体设计和原理上对几个图片缓存进行对比,没用到他们的朋友也可以了解他们在某些特性上的实现. 上 ...
- Android ImageCache图片缓存,使用简单,支持预取,支持多种缓存算法,支持不同网络类型,扩展性强
本文主要介绍一个支持图片自动预取.支持多种缓存算法的图片缓存的使用及功能.图片较大需要SD卡保存情况推荐使用ImageSDCardCache. 与Android LruCache相比主要特性:(1). ...
- 【Java/Android性能优5】 Android ImageCache图片缓存,使用简单,支持预取,支持多种缓存算法,支持不同网络类型,扩展性强
本文转自:http://www.trinea.cn/android/android-imagecache/ 主要介绍一个支持图片自动预取.支持多种缓存算法.支持二级缓存.支持数据保存和恢复的图片缓存的 ...
- Android开源项目发现--- 工具类图片缓存篇(持续更新)
1. Android-Universal-Image-Loader 图片缓存 目前使用最广泛的图片缓存,支持主流图片缓存的绝大多数特性. 项目地址:https://github.com/nostra1 ...
- Android 开源项目android-open-project工具库解析之(一) 依赖注入,图片缓存,网络相关,数据库orm工具包,Android公共库
一.依赖注入DI 通过依赖注入降低View.服务.资源简化初始化.事件绑定等反复繁琐工作 AndroidAnnotations(Code Diet) android高速开发框架 项目地址:https: ...
- Android:ViewPager扩展的具体解释——导航ViewPagerIndicator(有图片缓存,异步加载图片)
我们已经用viewpager该. github那里viewpager扩展,导航风格更丰富.这个开源项目ViewPagerIndicator.非常好用,但样品是比较简单,实际用起来是非常不延长.例如,在 ...
随机推荐
- ZigZag Conversion
The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like ...
- Wijmo 5 与Breeze 的组合,及与METRONIC 的集成
1.Wijmo 5 是支持ANGULARJS 的HTML5 控件 http://wijmo.gcpowertools.com.cn/ 官方试用版 C1Wijmo-Eval_5.20151.42 ...
- 【leetcode】Binary Tree Zigzag Level Order Traversal
Binary Tree Zigzag Level Order Traversal Given a binary tree, return the zigzag level order traversa ...
- Python模块之optparse
参考: http://www.cnblogs.com/captain_jack/archive/2011/01/11/1933366.html https://docs.python.org/2/li ...
- ffmpeg-20160726-bin.7z
ESC 退出 0 进度条开关 1 屏幕原始大小 2 屏幕1/2大小 3 屏幕1/3大小 4 屏幕1/4大小 S 下一帧 [ -2秒 ] +2秒 ; -1秒 ' +1秒 下一个帧 -> -5秒 f ...
- java 入门 第二季1
(1). 类和对象(java 语言是面向对象的) 1). 类是对象的类型 具有相同的属性和方法的一组对象的集合 类:属性和方法 定义类: 类名 属性 方法 //class为关键字 2.定义类时,首字母 ...
- C++基础(纯虚函数与抽象类)
C++基础之纯虚函数与抽象类 引言 纯虚函数在C++编程中的地位很重要,其关联到了设计模式中"接口"的概念. 语法 纯虚函数的语法: 1. 将成员函数声明为virtual 2. ...
- windows编程中关于“关闭窗口无法退出进程”的解决方法
一般会出现如下两种情况 1.WinMain函数中,最后阶段接收消息队列循环中,调用的GetMessage函数参数提供错误 如: while (GetMessage(&msg,hwnd, 0, ...
- Windows update 失败的解决方案
由于刚刚装了新的系统,结果在电脑用了刚刚一周之后,我打开了自动更新,结果电脑就一直处于更新状态中.期初我以为是更新比较慢,等等可能就结束了,结果等了足足一晚上,到了第二天,电脑显示是:配置更新失败,正 ...
- 【leetcode】Subsets (Medium) ☆
Given a set of distinct integers, S, return all possible subsets. Note: Elements in a subset must be ...