如果你们去看下 Bitmap.decodeResource() 源码,你们会发现,系统在加载 res 目录下的资源图片时,会根据图片存放的不同目录做一次分辨率的转换,而转换的规则是:
新图的高度 = 原图高度 * (设备的 dpi / 目录对应的 dpi )
新图的宽度 = 原图宽度 * (设备的 dpi / 目录对应的 dpi )
目录名称与 dpi 的对应关系如下,drawable 没带后缀对应 160 dpi:
所以,我们来看下序号 2 的实验,按照上述理论的话,我们来计算看看这张图片的内存大小:
转换后的分辨率:1080 * (240/160) * 452 * (240/160) = 1620 * 678
显然,此时的分辨率已不是原图的分辨率了,经过一层转换,最后计算图片大小:
1620 * 678 * 4B = 4393440B ≈ 4.19MB
这下知道序号 2 的实验结果怎么来的了吧,同样的道理,序号 3 资源目的是 hdpi 对应的是 240,而设备的 dpi 刚好也是 240,所以转换后的分辨率还是原图本身,结果也才会是 1.86MB。
小结一下:
位于 res 内的不同资源目录中的图片,当加载进内存时,会先经过一次分辨率的转换,然后再计算大小,转换的影响因素是设备的 dpi 和不同的资源目录。
分析点3基于分析点 2 的理论,看下序号 5,6,7 的实验,这三个实验其实是用于跟序号 2,3,4 的实验进行对比的,也就是这 6 个实验我们可以得出的结论是:
同一图片,在同一台设备中,如果图片放在 res 内的不同资源目录下,那么图片占用的内存空间是会不一样的
同一图片,放在 res 内相同的资源目录下,但在不同 dpi 的设备中,图片占用的内存空间也是会不一样的
所以,有可能出现这种情况,同一个 app,但跑在不同 dpi 设备上,同样的界面,但所耗的内存有可能是不一样的。
为什么这里还要说是有可能不一样呢?按照上面的理论,同图片,同目录,但不同 dpi 设备,那显然分辨率转换就不一样,所耗内存应该是肯定不一样的啊,为什么还要用有可能这种说辞?
emmm,继续看下面的分析点吧。
分析点4序号 8,9 的实验,其实是想验证是不是只有当图片的来源是 res 内才会存在分辨率的转换,结果也确实证明了,当图片在磁盘中,SD 卡也好,assert 目录也好,网络也好(网络上的图片其实最终也是下载到磁盘),只要不是在 res 目录内,那么图片占据内存大小的计算公式,就是按原图的分辨率 * 像素点大小来。
其实,有空去看看 BitmapFactory 的源码,确实也只有 decodeResource() 方法内部会根据 dpi 进行分辨率的转换,其他 decodeXXX() 就没有了。
那么,为什么在上个小节中,要特别说明,即使同一个 app,但跑在不同 dpi 设备上,同样的界面,但所耗的内存有可能是不一样的。这里为什么要特别用有可能这个词呢?
是吧,大伙想想。明明按照我们梳理后的理论,图片的内存大小计算公式是:分辨率*像素点大小,然后如果图片的来源是在 res 的话,就需要注意,图片是放于哪个资源目录下的,以及设备本身的 dpi 值,因为系统取 res 内的资源图片会根据这两点做一次分辨率转换,这样的话,图片的内存大小不是肯定就不一样了吗?
emmm,这就取决于你本人的因素了,如果你开发的 app,图片的相关操作都是通过 BitmapFactory 来操作,那么上述问题就可以换成肯定的表述。但现在,哪还有人自己写原生,Github 上那么多强大的图片开源库,而不同的图片开源库,内部对于图片的加载处理,缓存策略,复用策略都是不一样的。
所以,如果使用了某个图片开源库,那么对于加载一张图片到内存中占据了多大的空间,就需要你深入这个图片开源库中去分析它的处理了。
因为基本所有的图片开源库,都会对图片操作进行优化,那么下面就继续来讲讲图片的优化处理吧。
图片优化有了上述的理论基础,现在再来想想如果图片占用内存空间太多,要进行优化,可以着手的一些方向,也比较有眉目了吧。
图片占据内存大小的公式也就是:分辨率*像素点大小,只是在某些场景下,比如图片的来源是 res 的话,可能最终图片的分辨率并不是原图的分辨率而已,但归根结底,对于计算机来说,确实是按照这个公式计算。
所以,如果单从图片本身考虑优化的话,也就只有两个方向:
降低分辨率
减少每个像素点大小
除了从图片本身考虑外,其他方面可以像内存预警时,手动清理,图片弱引用等等之类的操作。
减少像素点大小