首先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源码:

[java] view
plain
 copy

  1. public static Bitmap decodeFile(String pathName, Options opts) {
  2. Bitmap bm = null;
  3. InputStream stream = null;
  4. try {
  5. stream = new FileInputStream(pathName);
  6. bm = decodeStream(stream, null, opts);
  7. } catch (Exception e) {
  8. /*  do nothing.
  9. If the exception happened on open, bm will be null.
  10. */
  11. } finally {
  12. if (stream != null) {
  13. try {
  14. stream.close();
  15. } catch (IOException e) {
  16. // do nothing here
  17. }
  18. }
  19. }
  20. return bm;
  21. }

decodeFileDescriptor的源码,可以找到native本地方法decodeFileDescriptor,通过底层生成bitmap

decodeFileDescriptor源码:

[java] view
plain
 copy

  1. public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
  2. if (nativeIsSeekable(fd)) {
  3. Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
  4. if (bm == null && opts != null && opts.inBitmap != null) {
  5. throw new IllegalArgumentException("Problem decoding into existing bitmap");
  6. }
  7. return finishDecode(bm, outPadding, opts);
  8. } else {
  9. FileInputStream fis = new FileInputStream(fd);
  10. try {
  11. return decodeStream(fis, outPadding, opts);
  12. } finally {
  13. try {
  14. fis.close();
  15. } catch (Throwable t) {/* ignore */}
  16. }
  17. }
  18. }
  19. private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,Rect padding, Options opts);

android bitmap的内存分配和优化的更多相关文章

  1. 谈谈Keil 中C51的内存分配与优化

    本帖最后由 Cresta 于 2014-1-21 10:49 编辑 看到这篇C51的内存分配和优化的文章,个人觉得分析的十分到位,在这里转给大家   C51的内存分配不同于一般的PC,内存空间有限,采 ...

  2. Keil C51内存分配与优化

    C51的内存分配不同于一般的PC,内存空间有限,采用覆盖和共享技术.在Keil编译器中,经过编译后,会形成一个M51文件,在其内部可以详细的看到内存的分配情况. C51内存常见的两个误区: A.变量超 ...

  3. 《Android虚拟机》--内存分配策略

    No1: Java在内存分配时会涉及到以下区域: 寄存器:我们在程序中无法控制 栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中 堆:存放用new产生的数据 静态域:存放在对 ...

  4. Android Bitmap那些事之如何优化内存

    前言:”安得广厦千万间,大庇天下寒士俱欢颜“——杜甫.在帝都住的朋友们都可能会遇到租房子困难的问题(土豪请无视),找房子真是力气活,还耗费时间,占用我宝贵的写博客时间,没办法,谁让咱没钱还想住的好点, ...

  5. Android Bitmap占用内存计算公式

    Android对各分辨率的定义 当图片以格式ARGB_8888存储时的计算方式 占用内存=图片长*图片宽*4字节 图片长 = 图片原始长 (设备DPI/文件夹DPI)  图片宽 = 图片原始宽(设备D ...

  6. Android开发中内存和UI优化

    1.内存||效率 GC这东西对于开发人员用起来比较爽,但对于技术总监或产品总监来说,他们并不在乎,在乎的是用户运行App的流畅度,待你开发完了,笑眯眯的走过来,让你测试N个适配器,烦都烦死你. 说到这 ...

  7. Android单个进程内存分配策略

    android不同设备单个进程可用内存是不一样的,可以查看/system/build.prop文件. # This is a high density device with more memory, ...

  8. Android性能优化:谈话Bitmap内存管理和优化

    最近除了那些忙着项目开发的事情,目前正在准备我的论文.短的时间没有写博客,今晚难得想总结.只要有一点时间.因此,为了凑合用,行.唠叨罗嗦,直接进入正题. 从事Android自移动终端的发展,想必是常常 ...

  9. 图片系列(6)不同版本上 Bitmap 内存分配与回收原理对比

    请点赞关注,你的支持对我意义重大. Hi,我是小彭.本文已收录到 GitHub · AndroidFamily 中.这里有 Android 进阶成长知识体系,有志同道合的朋友,关注公众号 [彭旭锐] ...

随机推荐

  1. Cocoa中层(layer)坐标系的极简理解

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) Cocoa层的坐标系一直理解的不清晰,现在把它整理总结一下: ...

  2. 悲观的并发策略——Synchronized互斥锁

    volatile既然不足以保证数据同步,那么就必须要引入锁来确保.互斥锁是最常见的同步手段,在并发过程中,当多条线程对同一个共享数据竞争时,它保证共享数据同一时刻只能被一条线程使用,其他线程只有等到锁 ...

  3. Afianl加载网络图片(续)

    上一篇已经讲了如何利用Afianl加载网络图片和下载文件,这篇文章将继续讲解使用Afinal加载网络图片的使用,主要结合listview的使用: 看效果图: listview在滑动过程中没用明显卡顿, ...

  4. FFmpeg源代码简单分析:结构体成员管理系统-AVOption

    ===================================================== FFmpeg的库函数源代码分析文章列表: [架构图] FFmpeg源代码结构图 - 解码 F ...

  5. java中List接口的实现类 ArrayList,LinkedList,Vector 的区别 list实现类源码分析

    java面试中经常被问到list常用的类以及内部实现机制,平时开发也经常用到list集合类,因此做一个源码级别的分析和比较之间的差异. 首先看一下List接口的的继承关系: list接口继承Colle ...

  6. MyBatis与MySQL交互

    MyBatis是我接触到的第一个框架,下面谈一谈我第一次使用MyBatis时的感悟. 首先是一些准备工作 下载相关的jar包.到GitHub上就行,上面有全面和完整的jar文件 在eclipse上安装 ...

  7. Android开发学习之路--Activity之生命周期

    其实这篇文章应该要在介绍Activity的时候写的,不过那个时候还不怎么熟悉Activity,还是在这里详细介绍下好了.还是参考下官方文档的图吧: 从上面的流程,我们可以看出首先就是打开APP,开始执 ...

  8. 【Android应用开发】 Android 崩溃日志 本地存储 与 远程保存

    示例代码下载 : http://download.csdn.net/detail/han1202012/8638801; 一. 崩溃日志本地存储 1. 保存原理解析 崩溃信息本地保存步骤 : -- 1 ...

  9. 后端分布式系列:分布式存储-HDFS DataNode 设计实现解析

    前文分析了 NameNode,本文进一步解析 DataNode 的设计和实现要点. 文件存储 DataNode 正如其名是负责存储文件数据的节点.HDFS 中文件的存储方式是将文件按块(block)切 ...

  10. 【C++知识点】单例模式的简单实现

    单例模式是最常见,也是使用最广泛的一种设计模式,其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享. 单例模式的实现方法有很多种,本文只给出一个最简单的实现,如下: ...