Android内存优化之图片优化

所以优化之前需要清楚2个基本原则:
  • 图片占用内存的大小与图片本身的大小没有直接关系;
  • WebP格式的图片虽然小,但占用的内存和其他格式无差别;
图片占用内存的大小

memorySize ≈ width * height * 每个像素需要的字节数

优化策略

既然需要的内存公式已得到,那优化就显而易见了,无非就是减小的这三个参数的值,具体的策略如下:

这里我们将图片分为2种情况来探讨:

drawable中的图片

单独探讨这种情况,是因为Android系统会对drawable中的图片进行缩放,缩放系数与设置的屏幕分辨率和drawable所表示的分辨率有关,具体的公式如下:

scale = 设备分辨率 / 资源目录分辨率 如:1080x1920的图片显示xhdpi中的图片,scale = 480 / 320 = 1.5

所以此时图片占用的内存大小为:

memorySize ≈ (width * scale) * (height * scale) * 每个像素需要的字节数
≈ width * height * scale ^ 2 * 每个像素需要的字节数

具体的缩放过程可参考Android中Bitmap内存优化

这里我们只探讨一下scale系数的影响因素:设备分辨率和资源目录分辨率。至于其他的可变因子会在另一种情况中介绍。设备分辨率我们没法改变,所以影响因素只有资源目录分辨率,也就是说,同一张图片,放在不同的drawable中,占用的内存大小不同。从公式可看出,使用同一个设备时,drawable表示的分辨率越高,则图片占用的内存越小,反之越大。所以,在做图片的兼容性时,如果只想使用一张图片,则应使用3倍甚至4倍的图片(3倍是主流机型,但在4倍手机上会被放大,图片可能失真),这样在低分辨率的手机上,不仅显示清晰,而且系统会自动进行缩放,从而确保占用较小的内存。

同样是存放图片的位置,为什么mipmap不在这种情况的考虑范围之内呢?因为mipmap是Android系统为了避免Launcher Icon变形而添加的资源目录,也就是说,mipmap中的图片不会被缩放。所以Google也不推荐将除Launcher Icon之外的图片放在mipmap目录中。

其他位置的图片

其他位置的图片包括mipmap, asset, 本地图片,网络图片等。这些位置的图片都有一个共同点——不会被缩放。所以只需要考虑如何改变图片分辨率和每个像素需要的字节数即可。

本地图片

本地图片通常都是通过Android提供的BitmapFactory来加载的, 这里看几个常用的API:

// 根据路径加载
public static Bitmap decodeFile(String pathName, Options opts);
// 加载drawable或mipmap中的图片
public static Bitmap decodeResource(Resources res, int id, Options opts)
// 根据字节流加载
public static Bitmap decodeByteArray(byte[] data, int offset, int length)
// 根据IO流加载
public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts)

图片的优化可通过Options参数来实现(Options的介绍可参考从fresco 看图片优化):

方式一:inSampleSize

inSampleSize可理解为图片的缩小比例,若inSampleSize小于1,则当做1处理。设置inSampleSize后,图片的宽度和高度将变成原来的1/inSampleSize, 其占用的内存空间将是原来的1/(inSampleSize ^ 2)。但是具体如何取值呢,可通过以下代码来获取:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.drawable.image, options);
options.inSampleSize = getSampleSize(options, 100, 100);
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.abc, options);
imageView.setImageBitmap(bitmap);

public static int getSampleSize(BitmapFactory.Options options, int viewWidth, int viewHeight) {
if (viewWidth == 0 || viewHeight == 0 || options == null) {
return 1;
}
int widthScale = options.outWidth / viewWidth;
int heightScale = options.outHeight / viewHeight;
Log.i(“out”, “width==” + widthScale + " heightScale==" + heightScale);
return widthScale >= heightScale ? heightScale : widthScale;
}

方式二:inDensity

inDensity相当于上面说的资源目录分辨率,前面说了,这里考虑的情况,图片不会被缩放,其原因就是inDensity和设备分辨率的取值是一致的,因为inDensity=设备分辨率,所以scale=1, 如果将inDensity设置为大于设备分辨率的值,那么图片就会被缩小。例如,当前的手机1dp=2px, 即2X屏幕,此时的inDensity为320, 如果将inDensity修改为480, scale=320f/480f=2/3, 那么图片所占用的内存将变成原来的4/9。

方式三:inPreferredConfig

inPreferredConfig的取值为Bitmap.Config类型(这里只考虑以下几种情况),它是一个枚举类型,用来设置每个像素需要的字节数:

ALPHA_8:占1个字节
RGB_565:占2个字节
ARGB_4444:占2个字节,已废弃,不推荐使用
ARGB_8888:32位真彩色,带透明度,占4个字节

显示图片时默认都是ARGB_8888,所以我们可通过inPreferredConfig的值进行内存优化。但实际上inPreferredConfig的取值对内存的影响并不是简单的Bitmap.Config.ALPHA_8占1个字节,ARGB_4444和RGB_565占2个字节,ARGB_8888占4个字节,而是与具体的图片格式有关:

  • inPreferredConfig对jpeg和gif格式的图片无作用,无论inPreferredConfig的值取什么,jpeg格式的图片每个像素始终占用4个字节,而gif格式的图片每个像素始终占1个字节;
  • 对于webp格式的图片,inPreferredConfig取值为RGB_565的时候,每个像素占用2个字节,其余的取值每个像素仍然占4个字节;
  • 对于png格式的图片,需要分png8, png24, png32三种情况来说。png8格式的图片每个像素占用的字节数随inPreferredConfig的取值而变化,取值为ALPHA_8时占用一个字节,取值为RGB_565时占用2个字节,取值为ARGB_4444或ARGB_8888时占用4个字节。png24格式的图片,当inPreferredConfig的取值为RGB_565时,每个像素占用2个字节,取其他的值(ALPHA_8, ARGB_4444和ARGB_8888)每个像素都占用4个字节。而对于png32格式的图片,inPreferredConfig的取值(ALPHA_8, RGB_565, ARGB_4444或ARGB_8888)对每个像素占用的字节数无影响。

所以,如果通过inPreferredConfig来优化图片的内存占用,就需要webp或png24格式的图片,png24与png32相比,也就是不支持透明度而已,对于大多数图片来说,两者没有明显的差别。当然,作为一种新的图片格式,web可认为是一种不错的选择。

注意: 9patch图虽然在使用时会根据View的尺寸进行放大,但其像素仍然不变,可视为普通图片来处理;

网络图片

网络图片通常我们都是使用开源库进行加载(这里顺便推荐一个好用的图片加载库ImageSet), inPreferredConfig的值通常可在初始化时进行配置,至于缩放,可让后台进行实现。即:根据图片的请求参数返回合适的尺寸。最大也只需要控件的大小即可,再大也没意义,不仅浪费流量,还占用内存。如果你的APP中有很多图片,那么可对图片的宽高根据设备的内存情况进行适当的缩小:

// 根据内存大小设置缩放系数
public static float getDefaultScale() {
float scale = 1.0f;
int totalMemorySize = AndroidPlatformUtil.getTotalMemorySize();
if (totalMemorySize >= 4) {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

尾声

面试成功其实都是必然发生的事情,因为在此之前我做足了充分的准备工作,不单单是纯粹的刷题,更多的还会去刷一些Android核心架构进阶知识点,比如:JVM、高并发、多线程、缓存、热修复设计、插件化框架解读、组件化框架设计、图片加载框架、网络、设计模式、设计思想与代码质量优化、程序性能优化、开发效率优化、设计模式、负载均衡、算法、数据结构、高级UI晋升、Framework内核解析、Android组件内核等。

不仅有学习文档,视频+笔记提高学习效率,还能稳固你的知识,形成良好的系统的知识体系。这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。

Android进阶学习资料库

一共十个专题,包括了Android进阶所有学习资料,Android进阶视频,Flutter,java基础,kotlin,NDK模块,计算机网络,数据结构与算法,微信小程序,面试题解析,framework源码!

image

大厂面试真题

PS:之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

《2017-2021字节跳动Android面试历年真题解析》

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

》**

[外链图片转存中…(img-kcATZoQb-1713791958731)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值