本文是Android Dev Summit 2019 系列视频的笔记第一篇。原视频见此。这个视频主要是把一些Android开发中有一部分所谓「优化」思路或者是先入为主的偏见逐一验证击破。
传闻:使用kotlin编写的app更加大,使用起来更慢?
用事实说话
Google Drive团队开始实践将项目中多达16000 多行的 java 代码转化成 kotlin 代码,最后的结果呢?请看图:
此次改动涉及16000多行代码,171 个代码文件,多达41 build target。但是对启动时间没有任何影响。如图所示,改动前后,启动时间均在2200多ms,其他性能数据也没有差别。但是他们代码行数将近减了25%。
这次总体改动后的收益为:
- 代码行数减少25%
- 通过工具测试后性能方面几乎没有差距
- 编译时间增长2%
- 编译后的代码大小增长2%
但是代码行数大量减少意味着代码维护工作以及代码出bug的几率也会大大减少。
所以综上所述,对于「使用kotlin编写的app更加大,使用起来更慢?」这个传闻是不实的。
传闻:通过Getters / Setters 方法访问model类属性会比直接访问属性更加耗时
如上图所示,大家可能以为通过get方法访问foo字段比起直接访问foo字段更加耗时,事实是这样的么?
然而作者通过专用的 Jetpack benchmark 库对两种形式进行测试,得到如下结果:
- 使用public 属性直接访问和通过getter方法访问完全没有区别;
- Android ART 虚拟机会将getter方法inline,导致最终和直接访问foo 属性效果一致
也就是说,在kotlin的世界里面:
tc.foo == tc.getFoo()
这里如果为了所谓的更快访问对应属性而将属性可见性改为 public 的行为不但没有带来性能上的优化,反而破坏了Java 的封装特性。
传闻:使用 Lambdas 表达式比使用内部类更好耗时
上面这两段代码在真实环境中会有区别吗?
同样,我们让数据说话。作者还是通过Jetpack benchmark 库对两种形式进行测试,得到如下结果:
- Lambdas 表达式的性能表现和内部类一致,几乎没有区别;
- 主要原因是Android 编译器在内部会先把Lambdas表达式翻译成匿名内部类,因此两者效果一致
基于上面的结论,作者推荐我们尽量使用lambdas表达式。这样能够保证代码更加简洁精确。
传闻:Android 虚拟机申请对象操作十分耗资源,我们应该尽量通过对象池来申请对象
坊间一直传闻Android开发中GC和对象申请都是十分耗时的操作,但是事实真的如此么?
首先Android平台的内存管理是一直都随着版本迭代在演进中。
如上图所示,Android 每个大版本都会在内存管理方面做一些优化,最新的几个版本里面的内存管理相对于最开始Android 2.0,3.0之类的远古版本应该是强大很多。Android 9.0 升级到 Android 10.0 以后所带来的GC优化效果如下图:
两个app在Android 9 和 Android 10 上面的GC提升都十分明显。
鉴于之前对Android 平台GC和对象创建都十分耗时的刻板印象,大家开始去寻找各种所谓优化的方式,其中最常见的就是使用对象池(Pool)来管理对象的创建和回收。
使用使用对象池(Pool)来管理对象的创建和回收就能减少GC的次数,提升app的用户体验么?
Pool<A> pool[] = new Pool<>[50];
void foo() {
A a = pool.acquire();
//.....
pool.release(a);
}
对了,一般实现这样的对象池还需要保证线程同步。
为了对比直接创建对象和使用对象池在CPU上面的开销,作者通过Jetpack benchmark 库对两种形式进行测试(ch),得到如下结果:
从结果可以看出两者区别不大。
那么是不是我们就可以得出「直接创建对象」和「使用对象池进行对象管理」在当前的Android 平台上面已经没什么区别了?
其实还是得看情况,如果我们使用对象池,我们需要知道使用对象池的一些弊端:
- 内存占用高
- 内存中所持有的对象数比我们实际需要的要多
- 实现一个高效的对象池管理类有一定门槛
但是碰到类似于大对象或者创建成本高的对象,使用对象池技术还是一个很不错的思路。
未完待续。。。
《“Android 开发流行的一些传闻(Android Dev Summit 2019)”》 有 1 条评论
哈哈哈哈,这篇文章真的是有意思。