在app中通常最占内存、占流量的元素就是图片了,图片往往又无处不在,特别是伴随着list,GridView或者ViewPager出现,这些图片随着你的滑动操作,时而出现在你的屏幕中,时而消失在屏幕之外。

对应滑出屏幕之外的图片,你可以缓存在内存中以便下次加载快速渲染,但这回增加内存的开销,你也可以立即释放掉这部分内存,但下次加载会变的很慢,因为来讲回收影响UI渲染,获取图片资源更加事一个耗时的过程。所以怎么样才能做到节省内存的开销又能提高加载速度?这是一个策略平衡问题,取决于你如何去使用 memory cachedisk cache来缓存Bitmap对象。

  • 使用Memory Cache(软引用、弱引用还在流行?)

memory cache 能使你从你的应用内存空间快速的访问到你的Bitmap对象。对应Bitmap的缓存,LruCache(Least Recently Used)应运而生,关于LruCache的介绍请看官方文档https://developer.android.com/reference/android/util/LruCache.html(FQ),简单的说
LruCache使用强引用方式把最近使用的内存对象使用LinkedHashMap存储起来,在你使用LruCache时需要设置一个最大缓存值,当内存即将接近这个最大值的时候,它将帮你把那些 Least Recently Used 的内存对象释放掉。在过去,一个通常的 memory cache 实现基本上是使用软引用或弱引用来缓存bitmap,然而现在已经不推荐使用了,为什么呢?一、从 android 2.3 以后,垃圾回收器对应软引用和弱引用的回收变动十分积极,这使得缓存的意义在极大程度上丢失;二, 在android 3.0 以前bitmpa的内存是存储在native内存中的,使得垃圾回收器很难回收,对应内存的预算很难把握。

使用LruCache,那么对于最大缓存值设置是一门艺术,你需要考虑很多因素。例如:

  • 你的 activity 使用了多少内存?
  • 有多少张图片会同时出现在你的屏幕中?
  • 你的缓存的图片被访问的频率是多少?
  • 你对图片显示清新度的取舍?

总之,没有一个固定的值适合所有的app,取决于你的app的具体身的很多因素,设置太小可能会降低你使用LruCache的福利,设置太大,在缓存峰值时候可能会引起OOM,这里有个例子参考:

private LruCache<String, Bitmap> mMemoryCache;
@Override
protected void onCreate(Bundle savedInstanceState) {...// Get max available VM memory, exceeding this amount will throw an// OutOfMemory exception. Stored in kilobytes as LruCache takes an// int in its constructor.final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);// Use 1/8th of the available memory for this memory cache.final int cacheSize = maxMemory / 8;mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {@Overrideprotected int sizeOf(String key, Bitmap bitmap) {// The cache size will be measured in kilobytes rather than// number of items.return bitmap.getByteCount() / 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);
} 

在这个例子中,使用了应用最大内存的1/8最为LruCache的最大值。

加载Bitmap对象到ImageView的经典模型

通常我们会先到 LruCache 中去检测一下存不存在,如果存在直接更新ImageView;如果不存在则开启一个线程去获取Bitmap对象(通常是到网络上获取,也有可能从disk中读取),然后再把这个Bitmap对象缓存到LruCache中。例如:

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);}
}
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {...// Decode image in background.@Overrideprotected Bitmap doInBackground(Integer... params) {final Bitmap bitmap = decodeSampledBitmapFromResource(getResources(), params[0], 100, 100));addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);return bitmap;}...
}

 

  • 使用disk缓存(硬盘缓存)

memory cache 在快速访问Bitmap上十分有用,然而我们不能一直依赖它,为什么呢?对于像GridView这样承载大量图片的组件来说,memory cache 会很快就被使用殆尽。另外当我们的应用被切换到后台的时候或者像来电话等这样的高优先级应用启用的时候,我们的app内存很可能会被回收,甚至LruCache对象也可能会销毁,一旦app再次切换到前台的话,所有的Bitmap对象都重新获取(通常网络请求),从而影响体验而且耗费流量。于是DiskLruCache出场了,关于DiskLruCache实现源码,有兴趣深究的可以点击这里查看。先来看一个在使用LruCache的基础上使用DiskLruCache的例子:

private DiskLruCache mDiskLruCache;
private final Object mDiskCacheLock = new Object();
private boolean mDiskCacheStarting = true;
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...// Initialize disk cache on background threadFile cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR);new InitDiskCacheTask().execute(cacheDir);...
}class InitDiskCacheTask extends AsyncTask<File, Void, Void> {@Overrideprotected Void doInBackground(File... params) {synchronized (mDiskCacheLock) {File cacheDir = params[0];mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE);mDiskCacheStarting = false; // Finished initializationmDiskCacheLock.notifyAll(); // Wake any waiting threads}return null;}
}class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {...// Decode image in background.@Overrideprotected Bitmap doInBackground(Integer... params) {final String imageKey = String.valueOf(params[0]);// Check disk cache in background threadBitmap bitmap = getBitmapFromDiskCache(imageKey);if (bitmap == null) { // Not found in disk cache// Process as normalfinal Bitmap bitmap = decodeSampledBitmapFromResource(getResources(), params[0], 100, 100));}// Add final bitmap to cachesaddBitmapToCache(imageKey, bitmap);return bitmap;}...
}public void addBitmapToCache(String key, Bitmap bitmap) {// Add to memory cache as beforeif (getBitmapFromMemCache(key) == null) {mMemoryCache.put(key, bitmap);}// Also add to disk cachesynchronized (mDiskCacheLock) {if (mDiskLruCache != null && mDiskLruCache.get(key) == null) {mDiskLruCache.put(key, bitmap);}}
}public Bitmap getBitmapFromDiskCache(String key) {synchronized (mDiskCacheLock) {// Wait while disk cache is started from background threadwhile (mDiskCacheStarting) {try {mDiskCacheLock.wait();} catch (InterruptedException e) {}}if (mDiskLruCache != null) {return mDiskLruCache.get(key);}}return null;
}// 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 getDiskCacheDir(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 dirfinal String cachePath =Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||!isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :context.getCacheDir().getPath();return new File(cachePath + File.separator + uniqueName);
}

注意:所有的disk读取操作都不应该发生在UI线程中,当从网络中获取Bitmap对象后应该同时保存到LruCache中和LruDiskCache中以便后续使用。

推荐:

内存泄露就应该用LeakCanary

Android如何缓存你的BITMAP对象相关推荐

  1. Android 那些你所不知道的Bitmap对象详解

    我们知道Android系统分配给每个应用程序的内存是有限的,Bitmap作为消耗内存大户,我们对Bitmap的管理稍有不当就可能引发OutOfMemoryError,而Bitmap对象在不同的Andr ...

  2. Android 从ImageView中获取Bitmap对象方法

    showImageView.setDrawingCacheEnabled(true);Bitmap bitmap=showImageView.getDrawingCache();showImageVi ...

  3. 【Android 内存优化】Bitmap 硬盘缓存 ( Google 官方 Bitmap 示例 | DiskLruCache 开源库 | 代码示例 )

    文章目录 一.Google 官方 Bitmap 相关示例参考 二.磁盘缓存类 DiskLruCache 三.磁盘缓存初始化 四.存储数据到磁盘缓存中 五.从磁盘缓存中读取数据 六. Android 1 ...

  4. Android性能优化系列:Bitmap

    文章目录 Bitmap 简介 Bitmap 的创建 不同系统版本 Bitmap 的内存分配策略 Bitmap 内存占用计算 在电脑查看的图片大小和运行内存大小区别 图片占用内存计算 Bitmap 内存 ...

  5. 【Android 内存优化】Bitmap 内存缓存 ( Bitmap 内存复用 | 弱引用 | 引用队列 | 针对不同 Android 版本开发不同的 Bitmap 复用策略 | 工具类代码 )

    文章目录 一.Bitmap 复用池 二.弱引用 Bitmap 内存释放 三.从 Bitmap 复用池中获取对应可以被复用的 Bitmap 对象 1.Android 2.3.3(API 级别 10)及以 ...

  6. Android截屏幕实现,截取屏幕为bitmap,并保存进手机图库,activity之间传递bitmap对象

    Android截屏幕实现,截取超过一屏幕的长图,截取当前显示的一屏幕的图片,截图屏幕内的部分图片,并保存到本地,更新到手机图库中:当然还有一种bitmap在activity之间传递的需求 踩坑-–&g ...

  7. 【Android 内存优化】Bitmap 图像尺寸缩小 ( 考虑像素密度、针对从不同像素密度资源中解码对应的 Bitmap 对象 | inDensity | inTargetDensity )

    文章目录 一.像素密度对解码图片的影响 二.不考虑像素密度会导致图片缩小尺寸不准确 三.DisplayMetrics 源码阅读.研究手机资源获取规则 四.像素密度参数设置取值 ( inDensity ...

  8. Android 通过Uri获取Bitmap对象

    1 package classExample.roseBulletEX3;import android.content.Context; import android.graphics.Bitmap; ...

  9. android——获取ImageView上面显示的图片bitmap对象

    获取的函数方法为:Bitmap bitmap=imageView.getDrawingCache(); 但是如果只是这样写我们得到的bitmap对象可能为null值,正确的方式为: imageView ...

最新文章

  1. 编译bluez-5.25 遇到的错误及解决方法
  2. hdu 2821 Pusher (dfs)
  3. WPF 和 windows Form Application的区别
  4. ubuntu下常用的抓包软件_macOS下最常用也最好用的几款解压缩软件,你值得拥有...
  5. sql多表查询的总结
  6. 有关Wiki的三个应用
  7. 网站发布助手V1.1 (去年写的简单小工具)
  8. Linux基本命令学习笔记
  9. html5 canvas 绘制圆角矩形
  10. CRC校验算法的Verilog实现
  11. Quartz定时任务动态数据库配置
  12. NOVA温控器参数笔记(十)(故障代码)
  13. P2837 [USACO08FEB]Dining Cows B 题解
  14. 程序设计大作业---词汇表生成
  15. matlab 祁彬彬,MATLAB 向量化编程基础精讲
  16. Windows 平台下局域网劫持
  17. oracle索引查询
  18. DB2处理数据由原来六小时优化到二十分钟(一)
  19. 真香!java设置全局变量
  20. nginx按日期统计访问人数uv和访问量pv

热门文章

  1. 为什么要发展鸿蒙,我们为什么需要鸿蒙?
  2. informix数据库 java 增删改查
  3. 作业四:个人项目-小学四则运算之JAVA版
  4. 工作中遇到很让人头疼的上司怎么办?
  5. 租客如何玩转物联网,打造智能新生活
  6. 订单中有订单详细实体类。保存订单
  7. Atitit 外包管理规范attilax总结
  8. QT编程之——使用全局变量
  9. django-dynamic-scraper(DDS)配置中的一些问题
  10. restframework之节流