Android中高效的显示图片之三——缓存图片
来源:互联网 发布:软件行业竞争格局 编辑:程序博客网 时间:2024/06/13 01:27
为了减少内存使用,这类控件都重复利用移出屏幕的子视图,如果你没有持用引用,垃圾回收器也会回收你加载过的图片。这种做法很好,但是如果想要图片加载快速流畅且不想当控件拖回来时重新运算获取加载过的图片,通常会使用内存和磁盘缓存。这节主要介绍当加载多张图片时利用内存缓存和磁盘缓存使加载图片时更快。
一、使用内存缓存
内存缓存以牺牲有限的应用内存为代价提供快速访问缓存的图片方法。LruCache类(有兼容包可以支持到API Level 4)很适合缓存图片的功能,它在LinkedHashMap中保存着最近使用图片对象的引用,并且在内容超过它指定的容量前删除近期最少使用的对象的引用。
注意:这前,很流行的图片缓存的方法是使用SoftReference和WeakReference,但是这种方法不提倡。因为从Android2.3(Level 9)开始,内存回收器会对软引用和弱引用进行回收。另外,在Android3.0(Levle 11)之前,图片的数据是存储在系统native内存中的,它的内存释放不可预料,这也是造成程序内存溢出的一个潜在原因。
为了给LruCache选择一个合适的大小,一些因素需要考虑:
》应用其它的模块对内存大小的要求
》有多少张图片会同时在屏幕上显示,有多少张图片需要提前加载
》屏幕的大小和密度
》图片的尺寸和设置
》图片被访问的频度
》平衡数量和质量,有时候存储大量的低质量的图片会比少量的高质量图片要有用
对于缓存,没有大小或者规则适用于所有应用,它依赖于你分析自己应用的内存使用确定自己的方案。缓存太小可能只会增加额外的内存使用,缓存太大可能会导致内存溢出或者应用其它模块可使用内存太小。
下面是为图片缓存设置LruCache的一个例子:
private LruCache mMemoryCache;@Overrideprotected 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);}
注意:这个例子中,应用内存的1/8用来做缓存。在普通hdpi设备上这个值通常为4M(32/8)。一个全屏的GridView,尺寸为800x480大小通常为1.5M左右(800*480*4sbytes),所以在内存中可以缓存2.5张图片。
当一个ImageView加载图片时,先检查LruCache。如果缓存中存在,会用它马上更新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; } ...}
二、使用磁盘缓存
内存缓存最加载最近访问过的图片有很大帮助,但你并不能依赖于图片会存在于缓存中。像GridView这样的控件如果数据稍微多一点,就可以轻易的把内存缓存用完。你的应用也有可能被其他任务打断,如电话呼入,应用在后台有可能会被结束,这样缓存的数据也会丢失。当用户回到应用时,所有的图片还需要重新获取一遍。
磁盘缓存可应用到这种场景中,它可以减少你获取图片的次数,当然,从磁盘获取图片比从内存中获取要慢的多,所以它需要在非UI线程中完成。示例代码中是磁盘缓存的一个实现,在Android4.0源码中(
libcore/luni/src/main/java/libcore/io/DiskLruCache.java),有更加强大和推荐的一个实现,它的向后兼容使在已发布过的库中很方便使用它。下面是它的例子:
private DiskLruCache mDiskCache;private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MBprivate static final String DISK_CACHE_SUBDIR = "thumbnails";@Overrideprotected 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线程中进行。当图片获取完成,把它同时缓存到内存中和磁盘中。
三、处理配置改变
private LruCache mMemoryCache;@Overrideprotected 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); }}
- Android中高效的显示图片之三——缓存图片
- Android中高效的显示图片之三——缓存图片
- Android中高效的显示图片之三——缓存图片
- Android中高效的显示图片之二——在非UI线程中处理图片
- Android中高效的显示图片之二——在非UI线程中处理图片
- Android中高效的显示图片之二——在非UI线程中处理图片
- android 优化那些事之图片缓存设计/如何高效的展示图片(三)
- Android中高效的显示图片
- Android中高效的显示图片
- Android中高效的显示图片
- Android中高效的显示图片
- Android中高效的显示图片
- Android中高效的显示图片
- android中高效显示图片
- Android高效的显示图片
- Android高效显示图片详解(三)
- Android高效显示图片详解(三)
- Android高效显示图片详解(三)
- Linux md5sum 文件校验
- android 4.0.4系统下实现apk的静默安装和启动
- Ubuntu Linux系统下apt-get命令详解
-
- Android--关于Cursor空指针的问题
- Android中高效的显示图片之三——缓存图片
- 2013年USNews美国大学计算机排名(研究生)
- 相似图片搜索的原理
- 迅雷 v7.2.13.3884 新春特别版
- Android 亲测源码分享
- stagefright 架构分析(六) 创建一个 Soft Decoder
- 温故知新--三层架构
- SPRINGMVC中配置FREEMARKER
- asp.net还原备份数据库(C#)