博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android图片加载内存占用分析
阅读量:6161 次
发布时间:2019-06-21

本文共 5194 字,大约阅读时间需要 17 分钟。

作为一名Android开发人员,你见得最多的大概就是res/drawable-[density]/ 文件夹了,现在又大概多了 res/mipmap-[density]/ 文件夹,这些文件夹通常用来存放图片资源文件,大家可能再熟悉不过了,现在我问你,一张大小为376.16K的480x800且位数为8的图片放在res/drawable-xxhdpi/ 文件夹下,在分辨率为1920*1080的手机上这张图片占用的内存是多少?

#1 概念厘清 如果对此比较有了解的小傻逼们,后面其实不需要看了,纯粹来扫一下盲,在正式分析之前,先来厘清一下相关的概念。

  • 1.1屏幕尺寸:按屏幕对角测量的实际物理尺寸,例如5.5英寸。Android 将所有实际屏幕尺寸分组为四种通用尺寸:小、 正常、大和超大;
  • 1.2分辨率:屏幕上物理像素的总数,添加对多种屏幕的支持时, 应用不会直接使用分辨率,而只应关注通用尺寸和密度组指定的屏幕尺寸及密度
  • 1.3屏幕密度:屏幕物理区域中的像素量,通常称为 dpi(每英寸点数)。屏幕密度越低在给定物理区域的像素就会较少。Android 将所有屏幕密度分为六组通用密度:ldpi( 低)、mdpi(中)、hdpi(高)、xhdpi(超高)、xxhdpi(超超高)和xxxhdpi(超超超高);
  • 1.4密度无关像素 (dp):在定义 UI 布局时应使用的虚拟像素单位。密度无关像素等于 160 dpi 屏幕上的一个物理像素,这是系统为mdpi(中)密度屏幕假设的基线密度。在运行时,系统根据使用中屏幕的实际密度按需要以透明方式处理dp单位的任何缩放 。dp单位转换为屏幕像素很简单: px = dp * (dpi / 160)。 例如,在 240 dpi屏幕上,1 dp等于1.5 物理像素。

对于我们的分析比较重要的就是屏幕密度。

#2 屏幕密度(dpi)对应关系

通用密度 ldpi mdpi(基线密度) hdpi xhdpi xxhdpi xxxhdpi
描述 超高 超超高 超超超高
大小(单位dpi) 120 160 240 320 480 640
缩放系数 0.75 1 1.5 2 3 4

六种通用密度之间遵循 3:4:6:8:12:16 的缩放比率,要注意的一点是xxxhdpi仅限启动器图标

#3 具体分析实现代码 代码很简单,就是用一个ImageView包含一张背景图片,然后通过转换为Bitmap查看占用内存大小。 布局文件activity_main.xml

复制代码

布局文件,就是一个ImageView控件,包含一张背景图。

MainAcivity.java

private void printBitmapSize(ImageView imageView) {        Drawable drawable = imageView.getDrawable();        if (drawable != null) {            BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;            Bitmap bitmap = bitmapDrawable.getBitmap();            //API 19            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){                size = bitmap.getAllocationByteCount();            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1){                //API 12                size = bitmap.getByteCount();            } else {                //earlier version                size = bitmap.getRowBytes() * bitmap.getHeight();            }            Log.d(TAG, " size = " + size);        } else {            Log.d(TAG, "Drawable is null !");        }    }复制代码

getAllocationByteCount()方法可以获取图片的实际占用内存大小,在此之前得介绍一种特殊的res/drawable-[density]/文件夹,就是res/drawable-nodpi/,不管当前屏幕的密度如何,系统都不会缩放以此限定符标记的资源。意思就是在这个文件夹中的图片按原样进行展示,不会像其它的res/drawable-[density]/那样改变文件的大小,我们就以此为基准进行分析。

#4 图片的实际内存占用 以实际例子作为分析,把一张大小为376.16K的480x800且位数为8的图片为例 图片的像素总数 480x800 = 384000 先使用压缩前的图片为例,分析图片占用的内存大小并打印出来

对图片进行压缩后进行同样得操作

很明显,压缩前后内存的占用大小同样为1536000(Byte),说明图片的磁盘占用大小与图片的内存或显存占用没有必然关系。从而说明压缩图片可以减少我们得apk大小,但是内存的占用是不会变小的,那么图片的内存占用与什么有关系呢?继续。。。

图片的内存占用大小为1536000(Byte),而图片的原始图片像素总数为384000,一眼看过去好像没啥关系,但是真相是384000 * 4 = 1536000(Byte),原始图片尺寸大小与最终的内存占用大小呈倍数的关系,所以在这里与内存占用大小有直接关系的就是原始图片尺寸大小(例如:480x800),道理我都懂,但是倍数关系是从哪里来的呢,这就要谈论到Bitmap的像素格式了。

Android系统支持4种格式的像素格式,源码在Bitmap.Config中

/**     * 可用的bitmap配置, 一个bitmap配置描述的是每个像素的存储格式,这将会影响到图片的质量 (颜色深     * 度) 以及显示透明/半透明颜色的能力     */    public enum Config {        // 这些枚举中的值必须要与Skia图像引擎的SkBitmap.h中对应值一一对应        /**         * 只有一个alpha通道          * 每个像素占1个字节         */        ALPHA_8     (1),        /**         *每个像素占用2个字节,只有RGB 3个通道,没有alpha 通道         * 红色的精度是5 bits, 绿色精度是6 bits,蓝色精度是5         */        RGB_565     (3),        /**         * 每个像素占用2个字节.          * (虽然占用内存只有 ARGB8888 的一半,不过已经被官方嫌弃)         */        @Deprecated        ARGB_4444   (4),        /**         * 每个像素占用4个字节. 每个通道 (RGB的3个通道和alpha         * 的1个透明度通道) 的进度是8bit (256个可能值)         * 这种配置是最灵活的, 质量最好,尽量使用这种格式.         */        ARGB_8888   (5);    }复制代码

由于官方默认使用ARGB_8888格式,导致图片的每个像素会占用4个Byte大小,所以最终的图片占用内存大小就是像素总数*像素格式,放到例子里头就是384000 * 4 = 1536000(Byte),成功接上去了,哈哈哈。。。

小结论:图片的直接内存占用和图片的像素总数和系统的像素格式相关,与磁盘存储的图片大小无关,其实与磁盘存储的图片位数也无关。

#5 Android对在res/drawable-[density]/ 文件夹中图片进行的骚操作 前面提到的图片实际占用内存大小,是很合理的,但是图片是放置在

前面也已经提到过res/drawable-nodpi/文件夹,在这个文件夹中的图片按原样进行展示,不会像其它的res/drawable-[density]/那样改变文件的大小,类似于从SD卡或者网络直接加载一张图片。 但是如果把图片放在其它的res/drawable-[density]/ 文件夹中的话,事情就会变得有些不一样了,系统会根据手机的屏幕密度来缩放对应文件夹中的图片。

下面就是测试结果,测试手机为360 vizza,手机分辨率为1920*1080,屏幕密度为480dpi,测试图片为480x800的图片。 先把图片放置drawable-ldpi中看占用内存大小,然后依次类比,得出最终的对比数据。

文件夹 文件夹dpi size(Byte)
drawable-ldpi 120 24576000
drawable-mdpi 160 13824000
drawable-hdpi 240 6144000
drawable-xhdpi 320 3456000
drawable-xxhdpi 480 1536000
drawable-xxxhdpi 640 864000

看到这个结果先不要慌,稳住,我们能赢...

经过前面的分析,我们知道在res/drawable-nodpi/下图片的占用内存为1536000(Byte),发现没有,我加粗的那一行数据中,也就在当图片放置在res/drawable-xxhdpi/文件夹下面时,图片所占用的内存也是1536000(Byte),而我们得测试机的屏幕密度就是480dpi,说明在对应屏幕密度的文件下获取图片时内存占用不会有变化。 而在把图片放置其他对应dpi文件夹下时,会出现图片内存占用出现不同程度的缩放,我们称与手机屏幕密度一致的文件夹称之为目标文件夹,当图片放置的文件夹对应密度比目标文件夹越小时,图片占用内存越大,当图片放置的文件夹对应密度比目标文件夹越大时,图片占用内存越小。

还记得这个表吗

通用密度 ldpi mdpi(基线密度) hdpi xhdpi xxhdpi xxxhdpi
描述 超高 超超高 超超超高
大小(单位dpi) 120 160 240 320 480 640
缩放系数 0.75 1 1.5 2 3 4

六种通用密度之间遵循 3:4:6:8:12:16 的缩放比率,内存占用缩放的秘密其实就是在这个缩放比率当中,最终的图片占用内存大小为: 图片最终内存=图片原始内存 * (手机屏幕密度/资源图片文件密度) ^ 2 其实就是图片宽和高都按缩放比率进行对应的缩放。

举个栗子: 当图片放置在res/drawable-ldpi/文件夹下时,图片内存为1536000*(480/120)^2=153600016=24576000(Byte); 当图片放置在res/drawable-xxhdpi/文件夹下时,图片内存为1536000(480/480)^2=15360001=1536000(Byte); 当图片放置在res/drawable-xxxhdpi/文件夹下时,图片内存为1536000(480/640)^2=1536000*0.5625=864000(Byte); 注:res/drawable-xxxhdpi/文件夹官方建议只能放启动图标,这里只是为了测试才放置测试图片。 对比一下上表对比数据,都一一对应,说明是ok的。

然后最终结论就是 1、图片的直接内存占用和图片的像素总数和系统的像素格式相关,与磁盘存储的图片大小无关,其实与磁盘存储的图片位数也无关,图片的直接内存占用大小为:像素总数 * 像素的格式(像素的格式其实就是确定了每个像素占用的字节数) 2、图片放置在res/drawable-[density]/ 文件夹中时,图片占用内存大小为:图片最终内存 = 图片原始内存 * (手机屏幕密度/资源图片文件密度) ^ 2

转载地址:http://urefa.baihongyu.com/

你可能感兴趣的文章
MyEclipse 报错 Errors running builder 'JavaScript Validator' on project......
查看>>
Skip List——跳表,一个高效的索引技术
查看>>
Yii2单元测试初探
查看>>
五、字典
查看>>
前端js之JavaScript
查看>>
Log4J日志配置详解
查看>>
实验7 BindService模拟通信
查看>>
scanf
查看>>
Socket编程注意接收缓冲区大小
查看>>
SpringMVC初写(五)拦截器
查看>>
检测oracle数据库坏块的方法
查看>>
SQL server 安装教程
查看>>
Linux下ftp和ssh详解
查看>>
跨站脚本功攻击,xss,一个简单的例子让你知道什么是xss攻击
查看>>
js时间和时间戳之间如何转换(汇总)
查看>>
js插件---图片懒加载echo.js结合 Amaze UI ScrollSpy 使用
查看>>
java中string和int的相互转换
查看>>
P1666 前缀单词
查看>>
HTML.2文本
查看>>
Ubuntu unity安装Indicator-Multiload
查看>>