背景
在某个版本应用上线后,偶然测得首页占用的内存非常的大而且一直不能回收掉,经过一轮的排查后最终确定是3张图片引起的!当时每张图片占用了将近20m内存。当时紧急处理好后还一直惦记着此事,后来对Android加载Bitmap的内存占用作了彻底的分析,跟踪了相关的源码,在这里总结一下。
图片加载测试
先抛开结论,现在先直观的看一下加载如下一张图片需要多少内存
![这里写图片描述](https://img-blog.csdn.net/20171013215542161?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYXhsY2hlbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
其中图片的宽高都为300像素
计算内存的方法采用 android.graphics.Bitmap#getByteCount
public final int getByteCount() {
// int result permits bitmaps up to 46,340 x 46,340
return getRowBytes() * getHeight();
}
预期占用的内存大小为
图片宽*图片高*表示每个像素点的字节数,即
![这里写图片描述](https://img-blog.csdn.net/20171013215655329?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYXhsY2hlbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
加载SD卡的图片
加载SD中的图片结果为
![这里写图片描述](https://img-blog.csdn.net/20171013220019909?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYXhsY2hlbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
assets的图片
加载asset目录中的图片结果为
![这里写图片描述](https://img-blog.csdn.net/20171013220019909?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYXhsY2hlbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
加载Resources的图片
-
drawable目录
![这里写图片描述](https://img-blog.csdn.net/20171013220118440?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYXhsY2hlbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
-
drawable-mdpi目录
![这里写图片描述](https://img-blog.csdn.net/20171013220118440?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYXhsY2hlbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
-
drawable-hdpi目录
![这里写图片描述](https://img-blog.csdn.net/20171013221041603?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYXhsY2hlbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
-
drawable-xhdpi目录
![这里写图片描述](https://img-blog.csdn.net/20171013220019909?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYXhsY2hlbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
-
drawable-xhhdpi目录
![这里写图片描述](https://img-blog.csdn.net/20171013220842679?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYXhsY2hlbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
-
drawable-xhhhdpi目录
![这里写图片描述](https://img-blog.csdn.net/20171013221125812?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYXhsY2hlbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
内存占用分析
理论上,300 * 300像素的图片,默认以4byte表示1个像素的情况下,占用的内存为
300 * 300 * 4 = 360000 byte
但是,实际上,只有从SD卡、assets目录、drawable-xhdpi目录下加载图片才等于理论数值,其他数值都不等!
等等!,从图片的大小看,不等于理论值的图片好像被放大或者缩小了?我们可以验证一下,把图片在内存中的实际宽高打印出来
SD卡的
![这里写图片描述](https://img-blog.csdn.net/20171013221210331?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYXhsY2hlbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
drawable-mdpi的
![这里写图片描述](https://img-blog.csdn.net/20171013221225767?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYXhsY2hlbg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
发现没有?在drawable-mdpi目录中的图片在加载内存中时的宽高都放大了两倍!!
其实,加载在SD卡和assets目录的图片时,图片的尺寸不会被改变,但是drawable-xxxdpi目录的照片的尺寸会被改变,这里篇幅所限,就不一一截图了,想验证的可以下载demo(文末给出链接)试验一下。至于尺寸改变的原因,下文会讨论,这里卖个关子。
查看源码
正所谓源码面前,了无秘密,欲知原理,还须从源码下手,首先查看BitmapFactory.java文件
BitmapFactory.decodeFile
BitmapFactory.decodeResourceStream
这两个方法的重载函数最终都会调用到
private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage,
Rect padding, Options opts);
这是一个本地方法,其相关实现在
frameworks/base/core/jni/android/graphics/BitmapFactory.cpp
打开文件,找到如下的方法,就是本地方法的实现
static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
jobject padding, jobject options) {
jobject bi