android bitmap的内存分配和优化
首先Bitmap在Android虚拟机中的内存分配,在Google的网站上给出了下面的一段话
大致的意思也就是说,在Android3.0之前,Bitmap的内存分配分为两部分,一部分是分配在Dalvik的VM堆中,而像素数据的内存是分配在Native堆中,而到了Android3.0之后,Bitmap的内存则已经全部分配在VM堆上,这两种分配方式的区别在于,Native堆的内存不受Dalvik虚拟机的管理,我们想要释放Bitmap的内存,必须手动调用Recycle方法,而到了Android
3.0之后的平台,我们就可以将Bitmap的内存完全放心的交给虚拟机管理了,我们只需要保证Bitmap对象遵守虚拟机的GC Root Tracing的回收规则即可。OK,基础知识科普到此。接下来分几个要点来谈谈如何优化Bitmap内存问题。
针对3.0版本的优化方案,请看以下代码,
private int mCacheRefCount = 0;//缓存引用计数器
private int mDisplayRefCount = 0;//显示引用计数器
...
// 当前Bitmap是否被显示在UI界面上
public void setIsDisplayed(boolean isDisplayed) {
synchronized (this) {
if (isDisplayed) {
mDisplayRefCount++;
mHasBeenDisplayed = true;
} else {
mDisplayRefCount--;
}
}
checkState();
}
//标记是否被缓存
public void setIsCached(boolean isCached) {
synchronized (this) {
if (isCached) {
mCacheRefCount++;
} else {
mCacheRefCount--;
}
}
checkState();
}
//用于检测Bitmap是否已经被回收
private synchronized void checkState() {
if (mCacheRefCount <= 0 && mDisplayRefCount <= 0 && mHasBeenDisplayed
&& hasValidBitmap()) {
getBitmap().recycle();
}
}
private synchronized boolean hasValidBitmap() {
Bitmap bitmap = getBitmap();
return bitmap != null && !bitmap.isRecycled();
}
通过引用计数的方法(mDisplayRefCount 与 mCacheRefCount)来追踪一个bitmap目前是否有被显示或者是在缓存中. 当下面条件满足时回收bitmap。
2.使用缓存,LruCache和DiskLruCache的结合
LruCache和DiskLruCache,大家一定不会陌生出于对性能和app的考虑,我们肯定是想着第一次从网络中加载到图片之后,能够将图片缓存在内存和sd卡中,这样,我们就不用频繁的去网络中加载图片,为了很好的控制内存问题,则会考虑使用LruCache作为Bitmap在内存中的存放容器,在sd卡则使用DiskLruCache来统一管理磁盘上的图片缓存。
3.SoftReference和inBitmap参数的结合
在第二点中提及到,可以采用LruCache作为存放Bitmap的容器,而在LruCache中有一个方法值得留意,那就是entryRemoved,按照文档给出的说法,在LruCache容器满了需要淘汰存放其中的对象腾出空间的时候会调用此方法(注意,这里只是对象被淘汰出LruCache容器,但并不意味着对象的内存会立即被Dalvik虚拟机回收掉),此时可以在此方法中将Bitmap使用SoftReference包裹起来,并用事先准备好的一个HashSet容器来存放这些即将被回收的Bitmap,有人会问,这样存放有什么意义?之所以会这样存放,还需要再提及到inBitmap参数(在Android3.0才开始有的,详情查阅API中的BitmapFactory.Options参数信息),这个参数主要是提供给我们进行复用内存中的Bitmap。
如果需要使用Bitmap的option参数还需要满足以下几个条件:
- Bitmap一定要是可变的,即inmutable设置一定为ture;
- Android4.4以下的平台,需要保证inBitmap和即将要得到decode的Bitmap的尺寸规格一致;
- Android4.4及其以上的平台,只需要满足inBitmap的尺寸大于要decode得到的Bitmap的尺寸规格即可;
4.降低采样率,inSampleSize的计算
直接上代码
public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
long totalPixels = width / inSampleSize * height / inSampleSize ;
final long totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels > totalReqPixelsCap) {
inSampleSize *= 2;
totalPixels /= 2;
}
}
return inSampleSize;
5.采用decodeFileDescriptor来编码图片,比直接使用decodeFile更省内存
查看BitmapFactory的源码,对比一下两者的实现,可以发现decodeFile()最终是以流的方式生成bitmap
decodeFile源码:
- public static Bitmap decodeFile(String pathName, Options opts) {
- Bitmap bm = null;
- InputStream stream = null;
- try {
- stream = new FileInputStream(pathName);
- bm = decodeStream(stream, null, opts);
- } catch (Exception e) {
- /* do nothing.
- If the exception happened on open, bm will be null.
- */
- } finally {
- if (stream != null) {
- try {
- stream.close();
- } catch (IOException e) {
- // do nothing here
- }
- }
- }
- return bm;
- }
decodeFileDescriptor的源码,可以找到native本地方法decodeFileDescriptor,通过底层生成bitmap
decodeFileDescriptor源码:
- public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
- if (nativeIsSeekable(fd)) {
- Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
- if (bm == null && opts != null && opts.inBitmap != null) {
- throw new IllegalArgumentException("Problem decoding into existing bitmap");
- }
- return finishDecode(bm, outPadding, opts);
- } else {
- FileInputStream fis = new FileInputStream(fd);
- try {
- return decodeStream(fis, outPadding, opts);
- } finally {
- try {
- fis.close();
- } catch (Throwable t) {/* ignore */}
- }
- }
- }
- private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,Rect padding, Options opts);
android bitmap的内存分配和优化的更多相关文章
- 谈谈Keil 中C51的内存分配与优化
本帖最后由 Cresta 于 2014-1-21 10:49 编辑 看到这篇C51的内存分配和优化的文章,个人觉得分析的十分到位,在这里转给大家 C51的内存分配不同于一般的PC,内存空间有限,采 ...
- Keil C51内存分配与优化
C51的内存分配不同于一般的PC,内存空间有限,采用覆盖和共享技术.在Keil编译器中,经过编译后,会形成一个M51文件,在其内部可以详细的看到内存的分配情况. C51内存常见的两个误区: A.变量超 ...
- 《Android虚拟机》--内存分配策略
No1: Java在内存分配时会涉及到以下区域: 寄存器:我们在程序中无法控制 栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中 堆:存放用new产生的数据 静态域:存放在对 ...
- Android Bitmap那些事之如何优化内存
前言:”安得广厦千万间,大庇天下寒士俱欢颜“——杜甫.在帝都住的朋友们都可能会遇到租房子困难的问题(土豪请无视),找房子真是力气活,还耗费时间,占用我宝贵的写博客时间,没办法,谁让咱没钱还想住的好点, ...
- Android Bitmap占用内存计算公式
Android对各分辨率的定义 当图片以格式ARGB_8888存储时的计算方式 占用内存=图片长*图片宽*4字节 图片长 = 图片原始长 (设备DPI/文件夹DPI) 图片宽 = 图片原始宽(设备D ...
- Android开发中内存和UI优化
1.内存||效率 GC这东西对于开发人员用起来比较爽,但对于技术总监或产品总监来说,他们并不在乎,在乎的是用户运行App的流畅度,待你开发完了,笑眯眯的走过来,让你测试N个适配器,烦都烦死你. 说到这 ...
- Android单个进程内存分配策略
android不同设备单个进程可用内存是不一样的,可以查看/system/build.prop文件. # This is a high density device with more memory, ...
- Android性能优化:谈话Bitmap内存管理和优化
最近除了那些忙着项目开发的事情,目前正在准备我的论文.短的时间没有写博客,今晚难得想总结.只要有一点时间.因此,为了凑合用,行.唠叨罗嗦,直接进入正题. 从事Android自移动终端的发展,想必是常常 ...
- 图片系列(6)不同版本上 Bitmap 内存分配与回收原理对比
请点赞关注,你的支持对我意义重大. Hi,我是小彭.本文已收录到 GitHub · AndroidFamily 中.这里有 Android 进阶成长知识体系,有志同道合的朋友,关注公众号 [彭旭锐] ...
随机推荐
- 【伯乐在线】FACEBOOK产品设计总监:设计APP时的14个必考题
最近看到Facebook产品设计总监Julie Zhuo拷问产品的14个问题,非常靠谱.其中有3个问题堪称致命拷问: 1.使用前:这款产品是如何吸引到你的注意力的?2.开始使用:使用这款产品是轻松愉悦 ...
- AMH 5.X下安装 Flarum
如果移动端访问不佳,请尝试–>Github版 背景 最近无意间发现几个开源软件的Bug反馈系统使用的是Flarum,Flarum是一款优雅简洁论坛软件,看起来还是相当不错的,一时抑制不住想要尝试 ...
- Eclipse中设置VM参数
eclipse.ini -Xms256m //设置堆最小值 -Xmx1024m //设置堆最大值 Eclipse 做JVM 的分析时,需要动态设置JVM的参数来进行各种测试, 可以在下图地方进行设置 ...
- linux配置java环境变量 转过几个,这个最详细和靠谱
一. 解压安装jdk 在shell终端下进入jdk-6u14-linux-i586.bin文件所在目录,之后会在当前目录下生成一个jdk1.6.0_14目录二. 需要配置的环境变量 1. PATH环境 ...
- Dynamics CRM 后台通过组织服务获取时间字段值的准确转换
做CRM开发的都知道,在系统时间字段的处理上是有讲究的,因为数据库中存的是UTC时间,CRM的界面时间字段会根据个人设置中的时区以及格式自动调整,这是最基本的一面,那还有很多使用时间的场景,比如脚本使 ...
- sql的简单提高效率方法
少用in操作(效率极差),尽量用表关联代替 select要指定列,不要*(*会读入所有数据,而指定列则只提取涉及的列,减少io) 尽量有where(减少读取量),where操作列尽量有索引(加快查询) ...
- Collections类解析
最常用的排序: 需要实现Comparable接口 1.什么是Comparable接口 此接口强行对实现它的每个类的对象进行整体排序.此排序被称为该类的自然排序 ,类的 compareTo 方法被称为它 ...
- Java进阶(三十五)java int与integer的区别
Java进阶(三十五)java int与Integer的区别 前言 int与Integer的区别从大的方面来说就是基本数据类型与其包装类的区别: int 是基本类型,直接存数值,而Integer是对象 ...
- UNIX网络编程——套接字选项(心跳检测、绑定地址复用)
/* 设置套接字选项周期性消息检测连通性 心跳包. 心博.主要用于长连接. * 参数:套接字, 1或0开启, 首次间隔时间, 两次间隔时间, 断开次数 */ void setKeepAlive( in ...
- 欢迎进入我的个人博客 anzhan.me
CSDN的博客依旧会更新,但是还是专注于技术. 个人的博客 http://anzhan.me 不单单会同步csdn的技术文章,还会有个人的更多私人的分享,包括旅行日记.欢迎各位朋友经常去看看,大家有私 ...