在Android中,Bitmap普遍是造成OOM的罪魁祸首,而且普遍都认为bitmap所占用的内存远大于Drawable,而且网上也提供了很多内存回收的方法以及建议,比如在activity销毁后主动去调用bitmap.recyle()方法等,那么到底是不是真的呢?我专门针对bitmap和drawble做了以下测试:
为了方便测出OOM,我专门将一张比较大的图片1270*1920放在了mdpi的文件夹下,而我的测试机是320dpi的,那么根据我上篇文章所讲到的,如果通过资源去加载bitmap,那么所占的内存会放大2倍,大概一张图在30M左右。
package com.example.imagetext;
import java.util.ArrayList;
import java.util.List;
import com.example.imagetext.R.drawable;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.WindowManager;
import android.widget.ImageView;
public class MainActivity extends Activity {
private List<Drawable> listd = new ArrayList<Drawable>();
private List<Bitmap> listb = new ArrayList<Bitmap>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listb.add(BitmapFactory.decodeResource(getResources(), R.drawable.bg_1));
listb.add(BitmapFactory.decodeResource(getResources(), R.drawable.bg_2));
listb.add(BitmapFactory.decodeResource(getResources(), R.drawable.bg_3));
listb.add(BitmapFactory.decodeResource(getResources(), R.drawable.bg_4));
Log.e("tttext","Bitmap占用的内存:"+getBitmapsize(listb.get(0))*4/1024/1024+"M");
Log.e("tttext", "当前APP分配的内存:"+Runtime.getRuntime().totalMemory()/1024/1024+"M"+"当前APP空闲内存:"+Runtime.getRuntime().freeMemory()/1024/1024+"M当前APP能够分配的最大内存:"+Runtime.getRuntime().maxMemory()/1024/1024+"M");
}
@SuppressLint("NewApi")
public long getBitmapsize(Bitmap bitmap) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
return bitmap.getByteCount();
}
// Pre HC-MR1
return bitmap.getRowBytes() * bitmap.getHeight();
}
/**
* 获取屏幕密度系数
*
* 例:0.75 / 1.0 / 1.5 / 2 / 3
*
*/
public float getScreenDensity(Context context) {
DisplayMetrics metric = new DisplayMetrics();
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
manager.getDefaultDisplay().getMetrics(metric);
return metric.density;
}
/**
* 获取屏幕密度
*
* 例:120 / 160 / 240 / 320(280-400) / 480(400-560)
*
*/
public int getScreenDensityDpi(Context context) {
DisplayMetrics metric = new DisplayMetrics();
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
manager.getDefaultDisplay().getMetrics(metric);
return metric.densityDpi;
}
}
这里我说下,bg_1,bg_2……都是同一张大图的不同命名,为什么不直接用一张图加载5次呢,那是因为Android系统从资源文件夹加载图片的时候会有缓存机制,这个等会再说。
打印结果如下:()
这里顺便说下,关于一个Android应用到底占多少内存的问题,其实这跟不同的手机厂商有关,不过一般都是16的倍数,早前的是16M,32M,后来则是64M,96M,甚至100多M居多,大家可以看到我这个手机一个APP能够分配的最大内存是196M。至于第一个参数,是当前APP申请到的内存,也就是说,这个196M是系统允许非配给你的上限,并不是一下子就全部分配给你,而是内存不足的时候去向JVM申请一部分,不足再申请一部分,如果超过196M则OOM。
当我再增加一张同样大小的图片的时候,其实我们可以自己算出来,174-15+149/4>192M,这个时候则报OOM了
:
那么是不是用Drawble代替bitmap是不是就可以避免OOM呢,
下面我们来测试一下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listd.add(getResources().getDrawable(R.drawable.bg_1));
listd.add(getResources().getDrawable(R.drawable.bg_2));
listd.add(getResources().getDrawable(R.drawable.bg_3));
listd.add(getResources().getDrawable(R.drawable.bg_4));
Log.e("tttext",
"当前APP分配的内存:" + Runtime.getRuntime().totalMemory() / 1024 / 1024 + "M" + "当前APP空闲内存:"
+ Runtime.getRuntime().freeMemory() / 1024 / 1024 + "M当前APP能够分配的最大内存:"
+ Runtime.getRuntime().maxMemory() / 1024 / 1024 + "M");
}
只是稍作修改:
然后打印结果如下:
而再加一张,同样出现OOM:
这说明在占用内存上,网上流传的bitmap远大于Drawable是不对的。
其实如果当bitmap和Drawable表示的都是一个位图(也就是我们熟知的图片)的时候,Drawable所占用的内存反而是要比bitmap高那么一点点的。
我们完全可以这样写:
Drawable drawable = getResources().getDrawable(R.drawable.bg_1);
Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();
Drawable只是一个抽象类,可以看做一个“图画”,当Drawable指向的是一个位图,也就是普通的那些png,jpg这类的图片获取到的时候,它其实是一个bitmapDrawble,当然如果指向的是一个shape,那么就是一个shapeDrawble(图形),由上面的两句代码可以知道,这个时候bitmapDrawble是持有bitmap的一个对象的,所以这个时候的Drawble所占用的内存只可能比bitmap大而不是小。
那么为什么会给我们造成一种Bitmap占用的内存远小大于Drawble的假象呢?
以及Imageview.setImageBitmap(),
setImageDrawable,
setImageResource
这些方法有什么区别呢?
还有到底什么时候该用Bitmap,什么时候该用Drawable,它们各有什么优点和缺点,该怎么避免OOM呢?
下一篇再详细记录下。