[AS2.3.3]Bitmap学习日记

前言

关于bitmap的学习。研究bitmap使用时,占用的内存和一些计算的原理,以及Bitmap优化相关内容


Bitmap占用内存

densityDpi、density说明

以下摘自官方api

    /**
     * The logical density of the display.  This is a scaling factor for the
     * Density Independent Pixel unit, where one DIP is one pixel on an
     * approximately 160 dpi screen (for example a 240x320, 1.5"x2" screen), 
     * providing the baseline of the system's display. Thus on a 160dpi screen 
     * this density value will be 1; on a 120 dpi screen it would be .75; etc.
     *  
     * <p>This value does not exactly follow the real screen size (as given by 
     * {@link #xdpi} and {@link #ydpi}, but rather is used to scale the size of
     * the overall UI in steps based on gross changes in the display dpi.  For 
     * example, a 240x320 screen will have a density of 1 even if its width is 
     * 1.8", 1.3", etc. However, if the screen resolution is increased to 
     * 320x480 but the screen size remained 1.5"x2" then the density would be 
     * increased (probably to 1.5).
     *
     * @see #DENSITY_DEFAULT
     */
    public float density;
    /**
     * The screen density expressed as dots-per-inch.  May be either
     * {@link #DENSITY_LOW}, {@link #DENSITY_MEDIUM}, or {@link #DENSITY_HIGH}.
     */
    public int densityDpi;

趋势如下表

density 1 1.5 2 2.5 3 3.5 4
densityDpi 160 240 320 400 480 560 640


我们可以通过代码获取到手机的density和densityDpi

//获取densityDpi
getResources().getDisplayMetrics().densityDpi
//获取density
getResources().getDisplayMetrics().density



Bitmap.Config

这边我们需要知道Bitmap有几个像素类型

参数 说明
ALPHA_8 表示8位Alpha位图
RGB_565 表示16位RGB位图
ARGB_4444 表示16位ARGB位图,这个从API 13 开始被废弃
ARGB_8888 表示32位ARGB位图


不同的配置将会影响图像的画质(色彩深度),位数越高画质越高。

建议:不需要透明度的图片可以使用RGB_565,需要透明度的图片使用ARGB_8888。


占用内存

测试手机和图片如下

测试手机为:小米5s

测试图片为:3200*1800的图片

那么我们就开始计算bitmap占用的内存到底有多少。

首先我们将图片放置在了xxhdpi的目录下,由获取了手机的densityDpi是480

densityDpi

放置位置

在用Bitmap的方法获取占用资源

    Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.sakura);
    iv.setImageBitmap(bitmap);
    if (bitmap != null) {
        Log.e("-s-btn1", "size = " + bitmap.getByteCount());
    }

打印结果

E/-s-btn1: size = 23040000

我们发现图片占用的内存有 23040000b 也就是21.97M左右的大小

那么我们就来看下为什么有这么大的内存了。

我们先看下getByteCount()方法

    /**
     * Returns the minimum number of bytes that can be used to store this bitmap's pixels.
     */
    public final int getByteCount() {
        // int result permits bitmaps up to 46,340 x 46,340
        return getRowBytes() * getHeight();
    }

在深入进去我们会发现这是一个jni方法,也就是计算占用内存的方法其实是用c写的

    public final int getRowBytes() {
        if (mRecycled) {
            Log.w(TAG, "Called getRowBytes() on a recycle()'d bitmap! This is undefined behavior!");
        }
        return nativeRowBytes(mNativePtr);
    }

这边就不在深入研究jni了,有想看下去的可以看这篇文章

通过后续jni的代码这边最后知道了计算逻辑是
计算的图片宽度为 int(图片宽度 * 手机dpi / 放置位置dpi + 0.5 )
计算的图片高度为 int(图片高度 * 手机dpi / 放置位置dpi + 0.5 )

然后占用内存为
计算的图片宽度 * 计算的图片高度 * 一个像素点占用的字节数

按照本次测试的图片和手机 我们的计算逻辑为
首先 放置在xxhdpi中 那么 放置位置dpi 为 480
然后是 手机的dpi 为 480
默认的像素类型为ARGB_8888

计算逻辑就是

计算的图片宽度 = int(3200 * 480 / 480f + 0.5) = int(3200.5) = 3200
计算的图片高度 = int(1800 * 480 / 480f + 0.5) = int(1800.5) = 1800
ARGB_8888占用字节为 4

占用内存 = 3200 * 1800 * 4 = 23040000

和打印获取的一模一样

之后我们将图片放到hdpi文件夹下

还是执行相同的代码,打印结果如下

E/-s-btn1: size = 92160000

整整差了4倍。我们也知道hdpi需要转换成xxhdpi也就是需要4倍的差值。


Bitmap优化探究

有了上面的内存占用计算。我们就可以来研究下如何优化Bitmap的内存使用。

图片需要放置在相应的dpi文件夹下

首先我们从上面内存计算的时候发现,放在不同的dpi文件夹下,居然就差了很大的内存占用。所以最优先的就是,将相应的图片放置到相应的dpi文件夹下。

bitmap.compress使用

首先说明下这个方法其实并没有压缩bitmap的内存占用大小

下面是第一个例子代码

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e("-s-", "====================================");
                Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.sakura);
                iv.setImageBitmap(bitmap);
                if (bitmap != null) {
                    Log.e("-s-btn1", "size = " + bitmap.getByteCount());
                    Log.e("-s-btn1", "size = " + bitmap.getByteCount() / 1024.00 / 1024 + "mb");
                }

                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                bitmap.compress(Bitmap.CompressFormat.JPEG,10,baos);
                byte[] bytes = baos.toByteArray();
                Log.e("-s-btn1", "compress size = " + bytes.length);

                bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
                iv2.setImageBitmap(bitmap);
                if (bitmap != null) {
                    Log.e("-s-btn1", "size2 = " + bitmap.getByteCount());
                    Log.e("-s-btn1", "size2 = " + bitmap.getByteCount() / 1024.00 / 1024 + "mb");
                }
            }
        });


log和图片如下

btn1

E/-s-: ====================================
E/-s-btn1: size = 23040000
E/-s-btn1: size = 21.97265625mb
E/-s-btn1: compress size = 85665
E/-s-btn1: size2 = 23040000
E/-s-btn1: size2 = 21.97265625mb

虽然我们看到读取的压缩length确实变小了,而且图片也变得模糊了。上面是原图,下面是compress过的图片。但是使用的时候还是一样的大小。

压缩图片的宽高

我们从上面就知道了,占用内存是=宽 * 高 * 像素占位,那么我么缩放图片的宽高就可以压缩图片的内存大小了。

代码如下

        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e("-s-", "====================================");
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inJustDecodeBounds = true;
                Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.sakura,options);
                int w = options.outWidth;
                int h = options.outHeight;
                String type = options.outMimeType;
                Log.e("-s-btn2", "w = " + w);
                Log.e("-s-btn2", "h = " + h);
                Log.e("-s-btn2", "type = " + type);

                options.inSampleSize = 3;
                options.inJustDecodeBounds = false;
                bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.sakura,options);
                w = options.outWidth;
                h = options.outHeight;
                type = options.outMimeType;
                Log.e("-s-btn2", "w2 = " + w);
                Log.e("-s-btn2", "h2 = " + h);
                Log.e("-s-btn2", "type2 = " + type);
                iv2.setImageBitmap(bitmap);
                if (bitmap != null) {
                    Log.e("-s-btn2", "size2 = " + bitmap.getByteCount());
                    Log.e("-s-btn2", "size2 = " + bitmap.getByteCount() / 1024.00 / 1024 + "mb");
                }
            }
        });


log如下

btn2

E/-s-: ====================================
E/-s-btn2: w = 3200
E/-s-btn2: h = 1800
E/-s-btn2: type = image/jpeg
E/-s-btn2: w2 = 1066
E/-s-btn2: h2 = 600
E/-s-btn2: type2 = image/jpeg
E/-s-btn2: size2 = 2558400
E/-s-btn2: size2 = 2.43988037109375mb

这边我们可以看到,相当明显的内存占用压缩了,而且对于大图来说,确实并没有造成很大视觉影响。

像素占位压缩

还是依据占用内存是=宽 * 高 * 像素占位,我们对最后的像素占位进行修改

代码如下

        btn3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e("-s-", "====================================");
                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inPreferredConfig = Bitmap.Config.RGB_565;
                Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.sakura,options);
                iv2.setImageBitmap(bitmap);
                if (bitmap != null) {
                    Log.e("-s-btn3", "size2 = " + bitmap.getByteCount());
                    Log.e("-s-btn3", "size2 = " + bitmap.getByteCount() / 1024.00 / 1024 + "mb");
                }

            }
        });

log如下

btn3

E/-s-: ====================================
E/-s-btn3: size2 = 11520000
E/-s-btn3: size2 = 10.986328125mb

整体缩小了一倍,因为从原来的ARGB_8888变成了RGB_565,就变小了一倍。


总结

对于bitmap优化可以结合缩放图和像素占位进行修改。
依据占用内存是=宽 * 高 * 像素占位只要修改其中的一个值,那么整个内存占用就会变小。


资料

计算bitmap占用内存大小
Android Bitmap 优化- 图片压缩

猜你喜欢

转载自blog.csdn.net/g777520/article/details/79657035
今日推荐