Android Bitmap开发之旅--OOM探索

1 问题

谈到Bitmap就必须和Android虚拟机内存扯上关系。在Android开发中,内存使用一直是围绕Bitmap开发的难点。虽然Java有专门的垃圾回收机制(GC),但是在Bitmap使用中,常常会遇到过OOM(out of memory)异常,相信有Android多媒体开发经验的朋友体会尤为深刻。

首先来分析一下OOM的原因,当我们使用BitmapFactory来decode一张3.5M大小的图片(3456*2304),我们decode出来的Bitmap的大小是多少呢?

Bitmap大小计算公式: Size = image.width * image.height * Config

(Config 参数为图片的编码器,常见的图片编译器有 ALPHA_8、RGB_565、ARGB_4444(Deprecated)、ARGB_8888)

RGB_565解码器占用2 byte       ARGB_8888 解码器占用4byte      

当图片采用ARGB_8888编码器时,Config为4,那么decode后的Bitmap大小为:3456 * 2304 * 4,换算成M为30.375M,My God整整是原来图片的8.6785倍,因此当我们使用Bitmap时稍有不慎,造成OOM简直成了理所当然了。

2 分析

难道编写Android的大牛们没有考虑到这个问题?

答案肯定是考虑到了的。在阐述接下来的问题之前,要给大家洗洗脑。很多人都认为Android应用所占用的内存就是虚拟机(Dalvik)所占用的内存。其实Android应用所占用的内存由Dalvik虚拟机内存和Native Memory内存组成。因此,当我们在进行多媒体开发的时候遇到OOM时,Java层代码已经做到perfect了(代码优化到极致),还是存在OOM异常的时候。可以利用Native Memory(本地内存)来解决OOM异常。

利用Native Memory?

我没听错吧?Davik 虚拟机内存和 C/C++的Native Memory共享?真是什么高难度技术,研究了半天源码,还是没有看出大牛们是怎么实现。具体实现的我们先放一边,(如果大家对内存共享感兴趣的话,可以去研究一下,顺便告诉我以下,在此先谢过了)我们的目的是怎样使用Native Memory内存。换句话说我们需要怎样将Bitmap所占用的内存挪到Native Memory内存中,这才是我们的重点。

查看应用占用的内存(Dalvik 虚拟机和Native Memory)大小的方式如下所示:

# adb shell dumpsys meminfo com.jony.actionbar

3 实现

首先我们来看看使用Dalvik虚拟机来的decode一张Bitmap所占用的内存大小。

使用图片信息: Storage: 3.5M图片(3456*2304)

网上有很多人说使用decodeStream()方法,不需要使用Java层decode Bitmap,因此可以节省Java层虚拟机的内存。我不知道这些人是凭空想象,还有有什么证据来说明他们的结论。根据我的实验以及源码的分析,BitmapFactory的decode方法都是调用的Java的Native方法。因此网上的这种说法正确性还有待考证。

方法一:

AssetManager am = getAssets();
InputStream is = am.open("high_pixel_img.jpg");
Bitmap bitmap = BitmapFactory.decodeStream(is);

Dalvik虚拟机占用的内存如图3-1所示:

Android Bitmap开发之旅--OOM探索_第1张图片
                                                                            图 3-1

(备注:其中红色标记的为当前Dalvik虚拟机所占内存)

使用#adb shell dumpsys meminfo com.jony.bitmaptest 命令查看Dalvik虚拟机内存和Native memory使用情况如图3-2所示:

Android Bitmap开发之旅--OOM探索_第2张图片

                                                                           图 3-2

方法二:

try {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inPreferredConfig = Config.ARGB_8888;
            options.inPurgeable = true;//允许可清除
            options.inInputShareable = true;// 以上options的两个属性必须联合使用才会有效果
            AssetManager am = getAssets();
            InputStream is = am.open("high_pixel_img.jpg");
            Bitmap bitmap = BitmapFactory.decodeStream(is,null,options);
            imageView.setImageBitmap(bitmap);
        } catch (IOException e) {
            e.printStackTrace();
        }

Dalvik虚拟机占用的内存如图3-3所示:
Android Bitmap开发之旅--OOM探索_第3张图片

                                                                                       图 3-3 

使用#adb shell dumpsys meminfo com.jony.bitmaptest 命令查看Dalvik虚拟机内存和Native memory使用情况如图所示:

Android Bitmap开发之旅--OOM探索_第4张图片

                                                                                 图 3-4


结合以上信息可以分析出:采用方法一Decode一张图片的时候Bitmap占用的是Dalvik虚拟机的内存;采用方法二Decode一张图片的时候Bitmap占用的Native memory的内存空间。因此在使用Bitmap的时候,可以根据业务的需求采用不同的内存占用方式,完美的解决OOM问题。



 

你可能感兴趣的:(java,android,虚拟机,编译器)