android图片资源规范以及Bitmap内存管理分析

0x01. 设备兼容性

0x01. 屏幕适配

  • 0x01. Convert dp units to pixel units
  px = dp * (dpi / 160)

dpi是根据每个设备不同的,必须从设备上获取.

DPI: Dots (or Pixels) Per Inch

计算公式

dpi = ( square_root ( horizontal_pixels ^ 2 + vertical_pixels ^ 2 )) / ( screen_size )

举例:
分辨率: 1920*1080
屏幕尺寸: 5.0 inch (对角线长度)

dpi = ( square_root ( 1080 ^ 2 + 1902 ^ 2 )) / ( 5.0 )
dpi = 441

➜  ~ python
Python 2.7.14 (default, Sep 23 2017, 22:06:14) 
[GCC 7.2.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 1080**2 + 1920**2
4852800
>>> import math
>>> math.sqrt(_)
2202.9071700822983
>>> float(_)/float(5.0)
440.58143401645964
>>> 

实际上dpi是480,为什么上面的计算是441呢? 因为5寸屏实际上显示用的部分是没有5寸的.

➜  ~ adb shell dumpsys | grep mBaseDisplay
    mBaseDisplayInfo=DisplayInfo{"内置屏幕", uniqueId "local:0", app 1080 x 1920, real 1080 x 1920, largest app 1080 x 1920, smallest app 1080 x 1920, mode 1, defaultMode 1, modes [{id=1, width=1080, height=1920, fps=60.0}], colorTransformId 1, defaultColorTransformId 1, supportedColorTransforms [{id=1, colorTransform=0}], rotation 0, density 480 (422.03 x 424.069) dpi, layerStack 0, appVsyncOff 0, presDeadline 17666667, type BUILT_IN, state OFF, FLAG_SECURE, FLAG_SUPPORTS_PROTECTED_BUFFERS}
➜  ~ 

PPI: pixels per inch

DP or DIP: Density-independent Pixels

举例: ImageView的宽度是60dp,那么在当前设备上换算成像素是多少?

根据之前的公式:
px = dp * (dpi/160)
px = 60 * (441/160)
px = 165

  • 0x02. 图片资源适配

Table 1. Configuration qualifiers for different pixel densities.

Density qualifierDescription
ldpiResources for low-density (ldpi) screens (~120dpi).
mdpiResources for medium-density (mdpi) screens (~160dpi). (This is the baseline density.)
hdpiResources for high-density (hdpi) screens (~240dpi).
xhdpiResources for extra-high-density (xhdpi) screens (~320dpi).
xxhdpiResources for extra-extra-high-density (xxhdpi) screens (~480dpi).
xxxhdpiResources for extra-extra-extra-high-density (xxxhdpi) uses (~640dpi).
nodpiResources for all densities. These are density-independent resources. The system does not scale resources tagged with this qualifier, regardless of the current screen’s density.
tvdpiResources for screens somewhere between mdpi and hdpi; approximately 213dpi.

Table 2. Android图片资源规格和放置目录.

规格描述
36x36 (0.75x)for low-density (ldpi)
48x48 (1.0x baseline)for medium-density (mdpi)
72x72 (1.5x)for high-density (hdpi)
96x96 (2.0x)for extra-high-density (xhdpi)
144x144 (3.0x)for extra-extra-high-density (xxhdpi)
192x192 (4.0x)for extra-extra-extra-high-density (xxxhdpi)

图片资源目录:

res/
  drawable-xxxhdpi/
    awesome-image.png
  drawable-xxhdpi/
    awesome-image.png
  drawable-xhdpi/
    awesome-image.png
  drawable-hdpi/
    awesome-image.png
  drawable-mdpi/
    awesome-image.png

应用图标目录:

res/
  mipmap-xxxhdpi/
    launcher-icon.png
  mipmap-xxhdpi/
    launcher-icon.png
  mipmap-xhdpi/
    launcher-icon.png
  mipmap-hdpi/
    launcher-icon.png
  mipmap-mdpi/
    launcher-icon.png
  • 0x03. 注意

如果把应该放到xxhdpi中的图片资源,错误的放到了drawable或者drawable-mdpi中.
同时当前设备的dpi是480,则该图片资源加载到内存中回被方大两倍(内存变大两倍).

0x02. 官方参考

https://developer.android.com/training/multiscreen/screendensities

https://developer.android.com/guide/practices/ui_guidelines/icon_design

0x02. Bitmap在android中的内存分析

0x01. Bitmap的加载方式

  • 0x01. 从xml资源文件加载
memory = width * height / (current res dip/device dpi)^2 * byte per pix

current res dip和资源的路径相关(drawable-xxhdpi),所以如果路径放错了后果很严重.

device dip,当前设备的dip.

byte per pix, 这个和bitmap的config值有关系,一个RGB在手机中站用的内存字节数.

因为Android需要根据不通目录中的资源文件进行自动适配,所以会自动的去计算缩放参数. 如果用户使用默认加载方式,不做任何处理的话.

  • 0x02. 从sdcard上加载

内存占用方式同上,关键在于是否人为设置bitmap的config参数,以及图片的缩放因子.

  • 0x03. 从网络加载

内存占用方式同上,关键在于是否人为设置bitmap的config参数,以及图片的缩放因子.

  • 0x04. 从assert目录加载

内存占用方式同上,关键在于是否人为设置bitmap的config参数,以及图片的缩放因子.

0x02. Bitmap内存分配

public void getBitmapDetail(String path){
    android.util.Log.i("Bitmap","getBitmapDetail() >>>>>>>");
    android.util.Log.i("Bitmap","File:"+path);
    java.io.File bitmapFile = new java.io.File(path);
    long length = bitmapFile.length();
    android.util.Log.i("Bitmap","File length:"+length);
    android.graphics.BitmapFactory.Options options = new android.graphics.BitmapFactory.Options();
    /**
     * options.inJustDecodeBounds = true;
     * 这里再decodeFile(),返回的bitmap为空,但此时调用options.outHeight时,已经包含了图片的高了
     */
    options.inJustDecodeBounds = true;
    android.graphics.Bitmap bitmap = android.graphics.BitmapFactory.decodeFile(tmpPath, options);
    /**
     *原始图片的高宽
     */
    android.util.Log.e("Bitmap", "Bitmap outHeight: " + options.outHeight);
    android.util.Log.e("Bitmap", "Bitmap outWidth: " + options.outWidth);


    android.graphics.drawable.BitmapDrawable bd = (android.graphics.drawable.BitmapDrawable) drawable;
    android.graphics.Bitmap bm = bd.getBitmap();
    android.graphics.Bitmap.Config config = bm.getConfig();

    int actor = 1;
    switch(config){
        case ALPHA_8:
            android.util.Log.i("Bitmap","ALPHA_8");
            actor = 1;
            break;
        case RGB_565:
            android.util.Log.i("Bitmap","RGB_565");
            actor = 2;
            break;
        case ARGB_4444:
            android.util.Log.i("Bitmap","ARGB_4444");
            actor = 2;
            break;
        case ARGB_8888:
            actor = 4;
            android.util.Log.i("Bitmap","ARGB_8888");
            break;
    }
    int rowBytes = bm.getRowBytes();
    android.util.Log.i("Bitmap","Bitmap rowBytes:"+rowBytes);
    int height = bm.getHeight();
    android.util.Log.i("Bitmap","Bitmap height:"+height);
    int mem = rowBytes*height*actor;
    android.util.Log.i("Bitmap","Bitmap mem:"+mem);
}

PS:一个图片资源占用总内存,不仅仅是bitmap分配的部分,还要包括bitmap对象,ImageView对象等

0x03. 注意事项

  • 0x01. 资源文件路径不要放错
  • 0x02. Bitmap接口的兼容性问题
  • 0x03. Bitmap的管理策略

0x04. 终极解决办法

使用SVG替换项目中的png相关资源,这样会引入项目的重构成本,设计师的工作量,以及设计和研发的沟通成本.

目前svg的方案已经成熟,研发需要处理的就是兼容性问题.

0x05. 参考

* Mannaging Bitmap Memory*
https://developer.android.com/topic/performance/graphics/manage-memory#recycle

相关源码分析的一篇文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值