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 qualifier | Description |
---|---|
ldpi | Resources for low-density (ldpi) screens (~120dpi). |
mdpi | Resources for medium-density (mdpi) screens (~160dpi). (This is the baseline density.) |
hdpi | Resources for high-density (hdpi) screens (~240dpi). |
xhdpi | Resources for extra-high-density (xhdpi) screens (~320dpi). |
xxhdpi | Resources for extra-extra-high-density (xxhdpi) screens (~480dpi). |
xxxhdpi | Resources for extra-extra-extra-high-density (xxxhdpi) uses (~640dpi). |
nodpi | Resources 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. |
tvdpi | Resources 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