Android Bitmap 计算内存

01.如何计算占用内存

  • 如果图片要显示下Android设备上,ImageView最终是要加载Bitmap对象的,就要考虑单个Bitmap对象的内存占用了,如何计算一张图片的加载到内存的占用呢?其实就是所有像素的内存占用总和:
  • bitmap内存大小 = 图片长度 x 图片宽度 x 单位像素占用的字节数
  • 起决定因素就是最后那个参数了,Bitmap’常见有2种编码方式:ARGB_8888和RGB_565,ARGB_8888每个像素点4个byte,RGB_565是2个byte,一般都采用ARGB_8888这种。那么常见的1080*1920的图片内存占用就是:1920 x 1080 x 4 = 7.9M

02.上面计算内存对吗

  • 说出我的结论:上面1.1这种说法也对,但是不全对,没有说明场景,同时也忽略了一个影响项:Density。接下来看看源代码。
  • inDensity默认为图片所在文件夹对应的密度;inTargetDensity为当前系统密度。
  • 加载一张本地资源图片,那么它占用的内存 = width * height * nTargetDensity/inDensity 一个像素所占的内存。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Nullable
public static Bitmap decodeResourceStream(@Nullable Resources res, @Nullable TypedValue value,
@Nullable InputStream is, @Nullable Rect pad, @Nullable Options opts) {
validate(opts);
if (opts == null) {
opts = new Options();
}

if (opts.inDensity == 0 && value != null) {
final int density = value.density;
if (density == TypedValue.DENSITY_DEFAULT) {
opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
} else if (density != TypedValue.DENSITY_NONE) {
opts.inDensity = density;
}
}

if (opts.inTargetDensity == 0 && res != null) {
opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
}

return decodeStream(is, pad, opts);
}
  • 正确说法,这个注意呢?计算公式如下所示

    • 对资源文件:width * height * nTargetDensity/inDensity * nTargetDensity/inDensity * 一个像素所占的内存;
    • 别的:width * height * 一个像素所占的内存;

03.一个像素占用内存

  • 一个像素占用多大内存?Bitmap.Config用来描述图片的像素是怎么被存储的?
    • ARGB_8888: 每个像素4字节. 共32位,默认设置。
    • Alpha_8: 只保存透明度,共8位,1字节。
    • ARGB_4444: 共16位,2字节。
    • RGB_565:共16位,2字节,只存储RGB值。

04.使用API获取内存

  • Bitmap使用API获取内存

    • getByteCount()

      • getByteCount()方法是在API12加入的,代表存储Bitmap的色素需要的最少内存。API19开始getAllocationByteCount()方法代替了getByteCount()。
    • getAllocationByteCount()

      • API19之后,Bitmap加了一个Api:getAllocationByteCount();代表在内存中为Bitmap分配的内存大小。
      1
      2
      3
      4
      5
      6
      7
      8
      public final int getAllocationByteCount() {
      if (mRecycled) {
      Log.w(TAG, "Called getAllocationByteCount() on a recycle()'d bitmap! "
      + "This is undefined behavior!");
      return 0;
      }
      return nativeGetAllocationByteCount(mNativePtr);
      }
  • 思考: getByteCount()与getAllocationByteCount()的区别?

    • 一般情况下两者是相等的;
    • 通过复用Bitmap来解码图片,如果被复用的Bitmap的内存比待分配内存的Bitmap大,那么getByteCount()表示新解码图片占用内存的大小(并非实际内存大小,实际大小是复用的那个Bitmap的大小),getAllocationByteCount()表示被复用Bitmap真实占用的内存大小(即mBuffer的长度)。
  • 在复用Bitmap的情况下,getAllocationByteCount()可能会比getByteCount()大。