Android 性能优化技术月报 | 2024 年 2 月
每个月都会有一些 Android 性能优化相关的优质内容发布,然而,碎片化阅读使得这些知识难以形成完整体系,且容易被遗忘。为解决这些问题,我决定尝试使用技术月报的形式,总结我在最近一个月内查阅的 Android 性能优化相关的优质内容。
月报的主要内容包括:整理展示我在最近一个月所查阅的 Android 性能优化领域的最新技术动态、精选博客,精选视频等内容。
精选博客
开发一款 SDK 需要注意哪些问题
有时我们需要将代码封装成 SDK 以供业务方调用,本文介绍了当我们开发一款 SDK 时应该注意的内容:
- Java 版本问题
- kotlin 版本问题
- minSdkVersion 版本问题
- 依赖混淆: 配置 proguard-rules.pro 混淆,keep 住对外的接口与方法,混淆实现类,SDK 发布时,不带上 sourceJar,外部只能查看 class 文件,进一步增加外部观摩 SDK 核心代码难度
- 向下兼容: SDK 对外方法尽量向下兼容,如遇到必须要移除的,可提前几个版本将方法标注 @Deprecated 过期,并提供的新的调用方法
- 非空处理: 对外接口必须标明入参与返回值的可空与非空,避免业务方发生 NPE 问题
- 清晰的注释
- 版本统一: 如果对外提供的 SDK 有多个依赖,并且不同版本可能会出现兼容性问题,则可以使用 Bom 来管理版本
- DIP 依赖倒置原则: SDK 提供抽象接口,业务方依赖接口调用
- 系统兼容判断: 如果 SDK 有针对一些系统版本兼容的处理,则不仅仅通过 Build.VERSION.SDK_INT 来判断机型版本就够了,还要判断应用的 targetSdkVersion 版本,避免业务方使用的 targetSdkVersion 比系统兼容版本低而导致没有效果的现象。
- 依赖冲突: 尽量减少三方库依赖, 如果三方库极其不稳定又不得不用,则可以下载其源码,更改包名,重新编译依赖。
心遇APP站内玩法H5体验优化实践
心遇 APP 在H5的开发过程中,实践了很多手段对H5进行性能提升,具体包括以下功能:
- 离线包拆包:对于玩法类H5,静态资源往往比较多,比如在我们的两个玩法里,图片经过压缩后,打包的总体积仍然会达到 10M 以上。由于离线包 diff 的版本可能有限,碰到客户端缓存的版本已经超过离线包 diff 版本限制时,则需要下载全量的离线包,这个全量包的流量不应该是用户应该承担的,所以我们选择对离线包进行“拆包”。
- 静态图片优化:类似于接口预加载的思路,我们使用 web worker 技术,将核心次级模块中的大图进行提前加载,由于 web worker 的非阻塞性和浏览器本身的资源缓存能力,这些次级模块的背景图会被提前加载并缓存在浏览器的内存中
- 动态图片优化:根据 UI 稿限制用户与后台上传图片的宽高,同时使用 CDN 裁剪,减少不必要的像素渲染。
几行代码轻松监控Android GC 情况
gc 的频率是影响 app 流畅性的一个重要指标,那么 Android 如何监控 gc 次数呢?
object GcWatcher {
private var gcWatcher: WeakReference<GcWatcherInternal>? = null
// 在你application启动的时候 init就行
@JvmStatic
fun init() {
if (gcWatcher == null) {
gcWatcher = WeakReference(GcWatcherInternal())
} else {
Log.v(TAG, "GCWatcher() is inited,dont need init again")
}
}
class GcWatcherInternal {
@kotlin.jvm.Throws(Throwable::class)
protected fun finalize() {
// 每次gc都会走这里 可以在这里统计你想要的次数/频率等信息
Log.v(TAG, "GcWatcherInternal detect vm do gc !!!!!")
gcWatcher = WeakReference(GcWatcherInternal())
}
}
}
Clean 架构下的现代 Android 架构指南
Clean 架构是 Uncle Bob 提出的一种软件架构,但是对于 Android 开发来说略显复杂,本文介绍了针对 Android 单体应用开发更贴切更精确的 Clean 架构图
- 依赖关系: Clean 架构基本准则是源码级别的内层不依赖外层,依赖关系永远是单向的,外层向内层依赖。
- Model (领域模型): 业务模型,或者叫领域模型,是根据软件业务设计出来的具体模型,一般来说会是个 data class,其中不包含任何业务逻辑,只是个单纯的模型对象。
- Adapter (数据适配器): 数据适配器层主要用来做数据转换,主要有两个职责:转换网络接口实体数据类和领域模型与领域模型之间的互相转换。
- Repo:对于我们 Android 开发来说,Repo 层应该是对网络接口或本地磁盘的数据读写的封装,对于 Repo 的使用者来说,不需要关注具体的实现,且 Repo 中一般不具备复杂的业务逻辑,只能包含简单的数据处理逻辑。
- UseCase (用例):UseCase 一般是指特定应用场景下的业务逻辑,用例引导了数据在模型之间的输入输出,并且指挥着业务实体利用其中的关键业务逻辑来实现用例的设计目标。因此,一个 UseCase 往往只包含一段具体的业务逻辑,他的输入是基本类型或者领域模型,输出也是,并且是幂等函数,也就是纯函数,所以 Google 建议我们每个 UseCase 只包含一个公开的函数。
- UiState: UiState 是用来描述当前 UI 状态的集合类,一般来说应该是个 data class。
- ViewModel: ViewModel 负责管理 UI 状态,执行对应的业务逻辑。
- UI 层: UI 层应该完全是数据驱动的,UI 层的作用就是百分之百的将 UiState 渲染出来,UiState 发生变化,UI 也跟着变化,这一点声明式 UI 框架做的很好。
精选视频
拼多多冷启真的秒开
本视频对比了拼多多,淘宝,京东,闲鱼等购物 app 的启动速度,可以看出拼多多在启动速度方面遥遥领先,同时介绍了一些常见的启动速度优化方法:
- Application attachBaseContext 优化:MultiDex 优化
- ContentProvider 优化:通过 App Startup 优化
- Application 优化: 精简 Application 启动任务及启动链路任务编排
- 首页优化:数据使用缓存,xml 优化或者预加载
- 后台任务优化: 减少后台线程不必要任务,线程收敛,gc 抑制
最后更新:
November 17, 2024