Android开发之图片详解(3)

在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了
Android开发之图片详解(3)_第1张图片

那么是不是用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:
Android开发之图片详解(3)_第2张图片

这说明在占用内存上,网上流传的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呢?
下一篇再详细记录下。

你可能感兴趣的:(Android开发之图片详解(3))