Android位图Bitmap在应用中的内存

1.非本地资源Bitmap内存的计算

首先是一个像素占用的内存

- ARGB_8888: 每个像素4字节. 共32位,默认设置。
- Alpha_8: 只保存透明度,共8位,1字节。
- ARGB_4444: 共16位,2字节。
- RGB_565:共16位,2字节,只存储RGB值。

不考虑采样率压缩,加载一张bitmap占用内存

memory = width*height*一个像素占的字节

当我们设置BitmapFactory.options.inSampleSize采样率时,宽和高都会相应的改变,那么加载一张bitmap的内存

memory = width/inSampleSize*height/inSampleSize*一个像素占的字节

所以我们在一般处理bitmap的时候,经常从options.inPreferredConfig和options.inSampleSize进行内存的优化。

2.APK本地资源Bitmap内存的计算

当使用本地资源的时候,还需要考虑Density的影响。对于本地资源在代码或xml中使用时,最终会进行BitmapFactory.decodeResource()得到bitmap,这时就会牵涉到当前的屏幕Density和资源drawable所在Density。

    BitmapFactory.java
    public static Bitmap decodeResourceStream(Resources res, TypedValue value,InputStream is, Rect pad, Options opts) {
       ...
        if (opts.inDensity == 0 && value != null) {
            final int density = value.density;
            if (density == TypedValue.DENSITY_DEFAULT) {
                //inDensity默认为图片所在文件夹对应的密度
                opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
            } else if (density != TypedValue.DENSITY_NONE) {
                opts.inDensity = density;
            }
        }
        if (opts.inTargetDensity == 0 && res != null) {
            //inTargetDensity为当前手机系统密度。
            opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
        }
        return decodeStream(is, pad, opts);
    }

    BitmapFactory.cpp 
    static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) {
        //初始缩放系数
        float scale = 1.0f;
        if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
            const int density = env->GetIntField(options, gOptions_densityFieldID);
            const int targetDensity = env->GetIntField(options, gOptions_targetDensityFieldID);
            const int screenDensity = env->GetIntField(options, gOptions_screenDensityFieldID);
            if (density != 0 && targetDensity != 0 && density != screenDensity) {
                //缩放系数是当前系数密度/图片所在文件夹对应的密度;
                scale = (float) targetDensity / density;
            }
        }

        ...

        //原始解码出来的Bitmap的宽高;
        int scaledWidth = decodingBitmap.width();
        int scaledHeight = decodingBitmap.height();
        //要使用缩放系数进行缩放,缩放后的宽高;
        if (willScale && decodeMode != SkImageDecoder::kDecodeBounds_Mode) {
            scaledWidth = int(scaledWidth * scale + 0.5f);
            scaledHeight = int(scaledHeight * scale + 0.5f);
        }    
      
        const float sx = scaledWidth / float(decodingBitmap.width());
        const float sy = scaledHeight / float(decodingBitmap.height());
        canvas.scale(sx, sy);
        canvas.drawARGB(0x00, 0x00, 0x00, 0x00);
        canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
        // now create the java bitmap
        return GraphicsJNI::createBitmap(env, javaAllocator.getStorageObjAndReset(),
            bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
    }

可以看到使用本地资源drawable时的bitmap内存占用,mTargetDensity->手机系统分辨率,density ->drawable所在文件夹分辨率

memory = width/(mTargetDensity/inDensity )*height/(mTargetDensity/inDensity )*一个像素占的字节

由此看出,在平时UI切图的时候,尽量给大分辨率的图片,同时一定要放到对应的文件夹中去。

结语

如有不同见解请留言探讨

    原文作者:进击de小黑
    原文地址: https://www.jianshu.com/p/6107143ade71
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞