Android 车载内存优化-图片

169 阅读3分钟

采集数据

台架设备硬件参数:

private fun display(){
    // 获取 DisplayMetrics 实例
    val displayMetrics = DisplayMetrics()
    // windowManager.defaultDisplay.getMetrics(displayMetrics) // 屏幕尺寸,去除导航栏
    windowManager.defaultDisplay.getRealMetrics(displayMetrics) // 屏幕真实尺寸

    val displayMetrics1 = resources.displayMetrics
    val widthPixels1 = displayMetrics1.widthPixels
    val heightPixels1 = displayMetrics1.heightPixels

    Log.i(TAG, "resources 屏幕宽度 (像素): $widthPixels1")
    Log.i(TAG, "resources 屏幕高度 (像素): $heightPixels1")
    Log.i(TAG, "------------------------------")

    val defaultDisplay = windowManager.defaultDisplay
    val width = defaultDisplay.width
    val height = defaultDisplay.height
    Log.i(TAG, "windowManager 屏幕宽度 (像素): $width")
    Log.i(TAG, "windowManager 屏幕高度 (像素): $height")
    Log.i(TAG, "------------------------------")

    // 获取屏幕宽度和高度(像素)
    val widthPixels = displayMetrics.widthPixels
    val heightPixels = displayMetrics.heightPixels

    // 获取屏幕宽度和高度(dp)
    val widthDp = widthPixels / displayMetrics.density
    val heightDp = heightPixels / displayMetrics.density

    // 获取屏幕密度
    val density = displayMetrics.density
    val densityDpi = displayMetrics.densityDpi

    // 打印屏幕尺寸和分辨率
    Log.i(TAG, "getRealMetrics 屏幕宽度 (像素): $widthPixels")
    Log.i(TAG, "getRealMetrics 屏幕高度 (像素): $heightPixels")
    Log.i(TAG, "屏幕宽度 (dp): $widthDp")
    Log.i(TAG, "屏幕高度 (dp): $heightDp")
    Log.i(TAG, "density  : $density")
    Log.i(TAG, "densityDpi  : $densityDpi")
}
  • 屏幕宽度 (像素): 1920
  • 屏幕高度 (像素): 1080
  • density : 1.3312501
  • densityDpi : 213

密度标识符和对应的密度值

dpi目录对应 DPI缩放值
drawable-ldpi1200.75
drawable-mdpi1601.0
drawable-hdpi2401.5
drawable-xhdpi3202.0
drawable-xxhdpi4803.0
drawable-xxxhdpi6404.0

图片内存的占用本质

图片占用空间的大小不是图片占用内存的大小,占用空间是在磁盘上占用的空间,内存大小是加载到内存中占用的内存大小。两个只是单位是一样的,本质不是一个概念。

  • 8.0 之前版本为 Bitmap 像素从 Java heap 申请内存。其核心原理是Bitmap 的像素是保存在 Java 堆上。
  • 8.0 版本为 Bitmap 像素从 Native heap 申请内存。其核心原理主要是通过 calloc 为 Bitmap 的像素分配内存,这个分配就在 Native 堆上

影响 Bitmap 占用内存的因素:

  • 图片最终加载的分辨率;
  • 图片的格式(PNG/JPEG/BMP/WebP);
  • 图片所存放的drawable目录;
  • 图片属性的色彩模式;
  • 设备的屏幕密度

台架设备是固定的,1920*1080 ,density = 1.3 , dpi = 213 ;这组数据没有完全匹配的 drawable 目录,但是放在 drawable-hdpi ,DPI =240 的目录下更合适、更省内存。


内存占用=1920×1080×4=7,136,640 bytes≈7.9 MB

// drawable-hdpi
`hdpi`对应的密度为 240dpi,通常在`hdpi`目录下,图片会被缩放为设备密度的比例。
由于设备的实际 dpi 为 213(根据 density 计算),因此需要进行缩放

缩放比例=213/2400.8875

计算缩放后的图片尺寸:
新宽度=1920×0.88751700 pixels 
新高度=1080×0.8875958 pixels

内存占用=1700×958×46,514,400 bytes≈6.2 MB

测试数据

在 Demo 应用中的数据

Demo 内存优化占比图.png

图片资源存放在 drawable、drawable-hdpi、drawable-xhdpi、drawable-xxhdpi 各个目录下,测试的 total、Java、native、code、other 占用的内存数据。

在应用中的数据

实际应用内存占比图.png

在 launcher 中不同的手顺,所对应的内存数据

优化后的数据

优化数据.png

优化操作:

  • 剔除一部分多余的依赖库
  • widget 资源图转 Webp
  • 图片资源存放在 drawable-hdpi 目录下

优化过程中,每次的内存快照图,都是在 5 分钟左右,更改变量再去测试下一次的内存占比。测试过程有其他事件打断,测试手顺没有保证绝对一致,数据可能存在一定的误差,但是总体来看,有所下降。内存优化空间在 50M~150M 左右。

待优化的点

  • 第三方库重复加载
  • 图片资源转 SVG
  • Layout 布局文件
  • SP 高频读写
  • 按需 选择合适的解码方式,ARGB_8888 或 RGB_565
  • 图片分辨率与实际显示 view(列帧动画方面采用的是全尺寸(550*450) 图片)