Android 性能优化五大误区和两大疑点!

ArrayList array = build();

int sum = array.stream().map(tc -> tc.foo).reduce(0, (a, b) -> a + b);

第一个 lambda 会将对象转换为整数,第二个 lambda 会将产生的两个值相加。

下面代码中,我们再将 lambda 表达式换成内部类:

ToyClassToInteger toyClassToInteger = new ToyClassToInteger();

SumOp sumOp = new SumOp();

int sum = array.stream().map(toyClassToInteger).reduce(0, sumOp);

这里,有两个内部类:一个是 toyClassToInteger,它可以将对象转换为整数,第二个 SumOp 用来做求和运算。

从语法上看,第一个带有 lambda 的示例显然更优雅,也更易读。那么,性能差异又如何呢?我们再次在 Pixel 3 上使用了 Jetpack Benchmark,也没有发现性能差异:

从图中可以看到,我们还定义了单独的外部 (top-level) 类一起来做比较,发现性能都没有什么差异,原因就是 lambda 表达式最终也会被转换为匿名内部类。因此,为了代码的简洁易读,在这种场景下 lambda 表达式就是第一选择。

误区四:对象分配开销过大,应该使用对象池

Android 内置了最先进的内存分配和垃圾回收机制,如下图所示,几乎每个版本的更新都在对象分配方面做各式各样的更新。

各个版本之间的垃圾收集性能都有显著的改善,如今,垃圾收集对应用程序的流畅已经几乎没有影响了。下图展示了 Google 官方在 Android 10 中对具有分代并发收集的对象收集所做的改进,新版本的 Android 11 中也有明显的改进。

在 GC 基准测试(例如 H2)中,吞吐量大幅提高了 170% 以上,而在实际应用(如 Google Sheets)中,吞吐量也提高了 68%。

如果认为垃圾收集效率低下并且内存分配负担很重,那么就相当于认为创建的垃圾越少,垃圾收集工作就越少,因此,代替每次使用时都创建新对象,我们可以维护一个经常使用的类型的对象池,然后从池中获取已创建的对象,如下:

Pool pool[] = new Pool<>[50];

void foo() {

A a = pool.acquire();

pool.release(a);

}

这里省略了代码细节,大体就是就是定义了一个 pool,从 pool 中获取对象,然后最终释放。

要测试这种场景,我们使用微基准测试 (microbenchmark):从池中测试分配对象的开销,以及 CPU 的开销,来确定垃圾回收是否会影响应用程序的性能。

在这种情况下,我们依然可以在装有 Android 10 的 Pixel 2 XL 上循环运行了数千次分配对象的代码,因为对于小型或大型对象,性能可能会有所不同,我们还通过添加不同的字段来模拟不同的对象大小,最终的开销结果如下:

用于垃圾回收的 CPU 开销的结果如下:

image

从图中可以看出,标准分配和池化对象之间的差异也很小,但是,当涉及到较大对象的垃圾回收时,池解决方案略微高一点。

这个结果并不意外,因为池化对象会增加应用的内存占用量,此时,应用突然占用了太多的内存,即使由于池化对象减少了垃圾回收调用的数量,每个垃圾回收调用的成本也更高,因为垃圾收集器必须遍历更多的内存才能确定哪些对象需要被收集,哪些对象需要保留。

那么,对象是否应该被池化,这还是主要取决于应用的需求。如果不考虑到代码复杂性,池化对象有如下缺点:

  • 提高内存占用量

  • 使对象存活变长

  • 需要非常完善的对象池机制

但是,池的方法对于大并且耗时的对象分配可能确实是有效的,关键是要记住在选择方案之前进行充分的测试。

误区五:debug 模式下进行性能分析

在 debug 的同时对应用进行性能分析非常方便,毕竟,我们通常也是在 debug 模式下进行编码的,并且,即使 debug 应用中的性能分析不准确,也可以更快地进行迭代修改提高效率,然后事实是并没有

为了验证这一误解,我们分析了 Activity 相关的常见操作过程过的测试结果,如下图:

在某些测试(例如反序列化)中,debug 与否对性能没有影响,但是,有些结果却有 50% 甚至以上的差别,我们甚至发现结果速度可能会慢 100% 的例子,这是因为 runtime 在 debug 模式下时对代码几乎没有优化,因此与用户在生产设备上运行的代码有很大不同。

在 debug 模式下进行性能分析的结果是可能会误导优化方向,导致浪费时间来优化不需要优化的内容。

疑点

现在,我们需要有意识的逃避上述提到的五大误区,下面我们再来看一下一些日常开发中不太明显,但我们经常会有的疑惑的问题,事实结果可能也与我们想的大相径庭。

疑点 1:Multidex:是否影响应用性能?

如今的 APK 文件越来越大,因为大型应用通常会超出 Android 限定的方法数量,从而使用 Multidex 方案打破传统的 dex 规范。

问题是,多少方法可以称之为多?而且如果应用包含大量 dex 是否对性能产生影响?很多时候我们也并不是因为应用太大,而是为了根据功能拆分 dex 文件来方便团队开发而使用 Multidex。

为了测试多个 dex 文件对性能的影响,我们使用了计算器应用,默认情况下,它只包含单个 dex 文件,我们可以根据其程序包边界将其拆分为五个 dex 文件,来根据功能部件模拟拆分。

首先,测试启动应用的性能,结果如下:

因此,拆分 dex 文件对此处并没有影响,对于其他应用,可能会因为某些因素而产生轻微的开销:应用程序的大小以及拆分方式。但是,只要合理地分割 dex 文件并且不添加成百个 dex 文件,对启动时间的影响应该不大。

接下来是 APK 的大小和内存消耗:

如图所示,APK 大小和应用的运行时内存占用量都略有增加,这是因为将应用程序拆分为多个 dex 文件时,每个 dex 文件都会有一些符号表和缓存表中的重复数据。

但是,我们可以通过减少 dex 文件之间的依赖关系来最大限度地避免这种情况,在这个案例中,并没有将 dex 包量化,我们可以使用 R8 和 D8 之类的工具合理分析项目结构并使用最小化的依赖关系,这些工具可以自动拆分 dex 文件,并帮助我们避免常见的错误,最大程度地减少依赖关系,如创建的 dex 文件数量不会超过指定的数量,并且不会将所有启动类都放置在主文件中。但是,如果我们对 dex 文件进行自定义拆分,请确保合理分析。

疑点 2:无用代码

使用 ART 这样的即时编译器的好处之一就是可以在运行时分析代码,并对其进行优化。有一种说法是,如果解释器 / JIT 系统没有对代码进行概要分析,就可能不会执行该代码。为了验证这一理论,我们检查了 Google 应用生成的 ART 配置文件,发现许多代码并没有被 JIT 做概要分析,这就表明许多代码实际上从未在设备上执行过。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

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

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

img

img

img

img

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

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

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

最后

跳槽季整理面试题已经成了我多年的习惯!在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

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

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

d面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中…(img-7NjYMVsu-1713759639417)]

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

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值