缓存你的BITMAP对象
在app中通常最占内存、占流量的元素就是图片了,图片往往又无处不在,特别是伴随着list,GridView或者ViewPager出现,这些图片随着你的滑动操作,时而出现在你的屏幕中,时而消失在屏幕之外。
对应滑出屏幕之外的图片,你可以缓存在内存中以便下次加载快速渲染,但这回增加内存的开销,你也可以立即释放掉这部分内存,但下次加载会变的很慢,因为来讲回收影响UI渲染,获取图片资源更加事一个耗时的过程。所以怎么样才能做到节省内存的开销又能提高加载速度?这是一个策略平衡问题,取决于你如何去使用 memory cache和disk cache来缓存Bitmap对象。
- 使用Memory Cache(软引用、弱引用还在流行?)
memory cache 能使你从你的应用内存空间快速的访问到你的Bitmap对象。对应Bitmap的缓存,LruCache(Least Recently Used)应运而生,关于LruCache的介绍请看官方文档https://developer.android.com/reference/android/util/LruCache.html(翻墙),简单的说
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) {
@Override
protected 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.
@Override
protected 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 thread
File cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR);
new InitDiskCacheTask().execute(cacheDir);
...
} class InitDiskCacheTask extends AsyncTask<File, Void, Void> {
@Override
protected Void doInBackground(File... params) {
synchronized (mDiskCacheLock) {
File cacheDir = params[0];
mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE);
mDiskCacheStarting = false; // Finished initialization
mDiskCacheLock.notifyAll(); // Wake any waiting threads
}
return null;
}
} class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...
// 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(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
synchronized (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 thread
while (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 dir
final 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中以便后续使用。
转自:大利猫
缓存你的BITMAP对象的更多相关文章
- Android如何缓存你的BITMAP对象
在app中通常最占内存.占流量的元素就是图片了,图片往往又无处不在,特别是伴随着list,GridView或者ViewPager出现,这些图片随着你的滑动操作,时而出现在你的屏幕中,时而消失在屏幕之外 ...
- 获取View的截图-将View转换为Bitmap对象
开发中,有时候需要获取View的截图来做动画来达到动画流程的目的 原理:将View的内容画到一个Bitmap画布上,然后取出 下面封装了一个从View生成Bitmap的工具类 /** * 将View转 ...
- 缓存依赖中cachedependency对象
缓存依赖主要提供以下功能:1.SQL 缓存依赖项可用于应用程序缓存和页输出缓存.2.可在 SQL Server 7.0 及更高版本中使用 SQL 缓存依赖项.3.可以在网络园(一台服务器上存在多个处理 ...
- Android中将Bitmap对象以PNG格式保存在内部存储中
在Android中进行图像处理的任务时,有时我们希望将处理后的结果以图像文件的格式保存在内部存储空间中,本文以此为目的,介绍将Bitmap对象的数据以PNG格式保存下来的方法. 1.添加权限 由于是对 ...
- android——获取ImageView上面显示的图片bitmap对象
获取的函数方法为:Bitmap bitmap=imageView.getDrawingCache(); 但是如果只是这样写我们得到的bitmap对象可能为null值,正确的方式为: imageView ...
- Activity跳转时传递Bitmap对象的实现
前言 相信大家可能都了解Activity跳转时我们是能够传递參数的,比方使用Intent传递或者Bundle来传递,把当前Activity的一些信息传递给将要跳转到的新的Activity.可是不知道大 ...
- android Bitmap(将视图转为bitmap对象)
1)从android的资源文件夹layout中加载xml布局文件,并把布局文件映射为Bitmap main.xml文件如下: <?xmlversion="1.0"encodi ...
- ResDrawableImgUtil【根据图片名称获取resID值或者Bitmap对象】
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 根据图片名称获取项目的res/drawable-xxdhpi中相应资源的ID值以及bitmap值的封装类. 效果图 代码分析 根据图 ...
- Android中Bitmap对象和字节流之间的相互转换
android 将图片内容解析成字节数组,将字节数组转换为ImageView可调用的Bitmap对象,图片缩放,把字节数组保存为一个文件,把Bitmap转Byte import java.io.B ...
随机推荐
- EXPLAIN PLAN获取SQL语句执行计划
一.获取SQL语句执行计划的方式 1. 使用explain plan 将执行计划加载到表plan_table,然后查询该表来获取预估的执行计划 2. 启用执行计划跟踪功能,即autotrace功能 3 ...
- 关于EndNote X6工具文献管理以及参考文献生成的使用
1 利用endnote下载参考文献 1.1在CNKI中查找文献,在前面打钩,这里显示已经选中两篇文献了。 然后选择导出文献——选择Endnote——导出并保存text文件 打开Endnote——imp ...
- Android中9patch图片格式(xx.9.png)介绍与制作详解
一:9patch图片介绍: android的.9.png是android系统中一种特殊的图片格式,专门用来用来处理图片大小变化后(如拉伸)的失真,不正常,如我们看到的qq聊天中的文字气泡,不管你输入的 ...
- myeclipse2013 for linux及其破解补丁百度网盘下载
FQ下载1.1G的东西不是开玩笑的,用GA下载了两回均失败,还是用了某某门在win下下载好的,来之不易,所以特意上传分享给大家,免得FQ.破解文件也一并附上: 注意:本人这个是在原文件基础上bzip2 ...
- Gnome 插件介绍
插件:Applications Menuhttps://extensions.gnome.org/extension/6/applications-menu/ TopIconshttps://exte ...
- case class inheritance
Scala 禁止case class inheritance case class Person(name: String, age: Int) case class FootballPlayer(n ...
- Samza的ApplicationMaster
当Samza ApplicationMaster启动时,它做以下的事情: 通过STREAMING_CONFIG环境变量从YARN获取配置信息(configuration) 在随机端口上 启动一个JMX ...
- 如何保护 .NET 应用的安全?
自从 Web 应用能给访问者提供丰富的内容之后,黑客们就把目光转向任何他们能够破坏,损毁,欺骗的漏洞.通过网络浏览器提供的应用越来越多,网络罪犯们可以利用的漏洞数量也呈指数增长起来. 大多数企业都依赖 ...
- 打车APP可能的盈利模式
贺滨,蓄力的芦苇 竺宇祺.郑子威.党培兵 等人赞同 按现在国内几十家类似start-ups的竞争态势看,嘀嘀现阶段应该还处于烧钱培育市场期. 斗胆想象一下可能的盈利模式: 前向收费(面向用户): 特权 ...
- Java发送post请求
package com.baoxiu.test; import java.io.BufferedReader;import java.io.InputStreamReader;import java. ...