Android位图Bitmap在应用中的内存

作者: 进击de小黑 | 来源:发表于2018-03-13 17:29 被阅读51次

    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切图的时候,尽量给大分辨率的图片,同时一定要放到对应的文件夹中去。

    结语

    如有不同见解请留言探讨

    相关文章

      网友评论

        本文标题:Android位图Bitmap在应用中的内存

        本文链接:https://www.haomeiwen.com/subject/yivzfftx.html