Jetpack 在三个主要领域的更新:
- 架构库及指南;
- 应用的性能优化;
- 用户界面库及指南。
- 其他关键更新的总结。
架构库及指南
应用架构库及其组件可以保证应用的健壮性、可测试性,以及可维护性。
数据持久化
Room 是我们推荐的数据持久化层,它在 SQLite 之上提供了一个抽象层,从而提高了平台的可用性和安全性。
在 Room 2.4 中,对 Kotlin Symbol Processing (KSP) 的支持已经稳定。在我们针对 Kotlin 代码的基准测试中,KSP 相对 KAPT 有两倍的速度提升。Room 2.4 还内建了对枚举和 RxJava3 的支持,同时也全面支持 Kotlin 1.6。
我们从 Room 2.5 开始使用 Kotlin 对整个库进行重写。这一改变可以为未来与 Kotlin 相关的改进打下基础,同时又与之前使用 Java 编程语言编写的版本二进制兼容。这一版本还内建了对 Paging 3.0 的支持,通过使用 room-paging 组件,可以使 Room 返回 PagingSource 对象。除此之外,由于 Room 支持使用多重映射 (嵌套的 Map 和 Array) 进行关联查找,开发者现在可以使用 JOIN 查询,而无需定义额外的数据结构。
使用多重映射作为返回值的关联查找方法
@Query("SELECT * FROM Artist
JOIN Object ON Artist.artistName =
Object.objectArtistName")
fun getArtistToObjects(): Map<Artist, List<Object>>
AutoMigrations 在更新中加入了对额外注解和属性的支持,从而进一步简化了数据库迁移。其中,@Database 注解新加入了一个属性,可以用于定义需要在哪两个版本间进行自动迁移。而当 Room 需要一些额外信息(如表或列的修改信息)时,可以使用 @AutoMigration 注解指定输入。
Database(
version = MyDb.LATEST_VERSION,
autoMigrations = {
@AutoMigration(from = 1, to = 2,
spec = MyDb.MyMigration.class),
@AutoMigration(from = 2, to = 3)
}
)
public abstract class MyDb
extends RoomDatabase {
...
DataStore
DataStore 库是一款健壮可靠的数据存储解决方案,它解决了 SharedPreferences 所存在的问题。如果想要了解如何在各种 SharedPreferences 的应用场景中使用这一强大的替代方案,您可以查看 MAD Skills: DataStore 系列文章和视频,其中包含了如何测试应用中 DataStore 库的使用情况、如何配合依赖注入使用 DataStore,以及如何从 SharedPreference 迁移至 Proto DataStore。
增量数据获取
Paging 库可以让您加载和显示整体数据中的一小部分,从而改善网络与系统资源的消耗。您可以配合 RecyclerViews 或 Compose lazy list 优雅地渐进加载应用数据。
Paging 3.1 为 Rx 和 Guava 集成提供了稳定支持,从而为 Paging 原生使用的 Kotlin 协程提供了 Java 版的替代方案。此版本还通过新的返回类型 LoadResult.Invalid 表示无效或过期的数据,从而改进了对无效竞争条件的处理。同时,该版本还通过新的 onPagesPresented 与 addOnPagesUpdatedListener API 改进了对无操作加载和操作空页面的处理。
如需了解有关 Paging 3 的更多信息,请参阅 Android 开发者网站中全新简化版的教程: Paging Basics Codelab,它描述了如何在包含列表的应用中集成 Paging 库。
定义应用内导航模型
Navigation 库是用于在应用中的目的地之间进行移动的框架。
Navigation 组件现已通过 navigation-compose 组件集成到了 Jetpack Compose 中,从而允许可组合函数作为您应用中的目的地。
经过优化的 Multiple Back Stacks 功能,更便于 Navigation 组件记录状态。NavigationUI 现在可以自动存储和恢复弹出目的地的状态,这意味着开发人员无需改动任何代码即可支持多返回栈。
Navigation-fragment 组件进一步增强了对大屏幕设备的支持,它在 AbstractListDetailFragment 中提供了一个预制的双窗格布局实现。这一 Fragment 使用 SlidingPaneLayout 管理一个列表窗格 (由您的子类管理),以及一个由 NavHostFragment 实现的详情窗格。
所有的 Navigation 组件现已使用 Kotlin 重写,并使用泛型改进了类的可空性,例如 NavType 的子类。
架构库指南
针对我们的核心架构库如何协同使用这一问题,如您想要了解更多信息,可以观看我们的视频与文章合集,这其中涵盖了现代 Android 开发最佳实践系列内容——MAD Skills:架构。
优化应用性能
通过使用性能库,您可以构建高性能的应用,并作出针对性的优化以维持其性能表现,从而获得更好的终端用户体验。
优化启动时间
应用的启动时间对用户体验影响巨大,特别是在应用安装完成后立即使用时尤为明显。为了提升首次启动时的体验,我们创建了 Baseline Profiles。Baseline Profiles 允许应用和库向 Android 运行时提供有关代码路径使用情况的元数据,从而确定提前编译的优先级。这一配置文件会对依赖库的数据进行聚合,以 baseline.prof 文件的形式放入应用的 APK 中,并且随后会在安装时用于实现应用的部分预编译以及用于静态链接库代码中。这会使您的应用加载的更快,并且可以在用户首次与应用交互时减少丢帧。
我们已经开始在 Google 内部使用 Baseline Profiles。Play Store 应用在接入 Baseline Profiles 后,搜索结果页初始页面的渲染时间减少了 40%。为了给终端用户提供更好的用户体验,一些流行的依赖库也已经加入了 Baseline Profiles,例如 Fragment 和 Compose。如果想要创建您自己的基线配置文件,您需要使用 Macrobenchmark 库。
Baseline Profiles / Macrobenchmark
Google 推荐我们使用 Jetpack 当中的 Macrobenchmark。它是 Android 里的一个性能优化库,借助这个库,我们可以:生成Baseline Profile文件。
@ExperimentalBaselineProfilesApi
@RunWith(AndroidJUnit4::class)
class BaselineProfileGenerator {
@get:Rule val baselineProfileRule = BaselineProfileRule()
@Test
fun startup() =
baselineProfileRule.collectBaselineProfile(packageName = "com.example.app") {
pressHome()
// This block defines the app's critical user journey. Here we are interested in
// optimizing for app startup. But you can also navigate and scroll
// through your most important UI.
startActivityAndWait()
}
}
唯一需要注意的,就是我们需要在 root 过后的 AOSP 9.0+ 的系统上才能采集到热点代码的信息。最终,Macrobenchmark 会把统计到的热点代码信息放到文件里。
/storage/emulated/0/Android/media/package.name.SampleStartupBenchmark_startup-baseline-prof.txt
我们拿到这个统计的文件,将其重命名为baseline-prof.txt,放到工程里去即可。
写入 baseline.prof
经过前面的分析,我们知道,baseline.prof 需要写入到系统特定的目录下,才能够引导 AOT 编译。这一点又是如何做到的呢?
这时候,我们需要用到另一个 Jetpack Library:ProfileInstaller。从它的名字,我们就能看出,它的功能就是:将 APK 当中的 baseline.prof 写入到系统目录下。
它的用法也很简单:
implementation "androidx.profileinstaller:profileinstaller:1.2.0-beta02"
引入依赖,这没什么好说的,常规操作。然后就是初始化设置
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data android:name="androidx.profileinstaller.ProfileInstallerInitializer"
tools:node="remove" />
</provider>
注意事项:在研究 Baseline Profiles 的过程中,我也发现了一些小细节,可能需要大家额外留意。
第一,由于 Android 手机有许多的厂商,每个厂商会对系统进行一些定制化,也许某些厂商会封死 Profile 文件的写入权限。即使这个方案无需 Google Play,但国内支持写入 Profile 的手机具体占多大的比例,我目前还没有数据,欢迎大家在使用了 Baseline Profile 以后来向我反馈。
第二,如何衡量 Baseline Profile 带来的性能提升?这一点, Macrobenchmark 也提供了相关的能力,具体可以看这个官方文档的链接。
第三,Debug 编译的 App,是不会进行 AOT 编译的,因此它的性能会比 release 低不少。
第四,baseline-prof.txt放的位置很关键,它必须跟AndroidManifest.xml是同级目录下。
第五,Baseline Profile 必须使用 AGP 7.1.0-alpha05 及以上的版本,7.3.0-beta01及以上对 App Bundle、多 Dex 应用的支持会更好。
第六,baseline-prof.txt 文件大小不得超过 1.5M,且,其中定义的规则不能太宽泛,否则可能反而降低性能。
检测您的应用
Macrobenchmark 库可以通过将 Jetpack 基准测试的覆盖范围扩展至更为复杂的用例,来帮助开发者更好的了解应用性能。这其中包含了应用启动及集成界面操作 (如滚动 RecyclerView 或运行动画)。Macrobenchmark 也可用于生成 Baseline Profiles。
Macrobenchmark 已经更新以提高测试速度,同时也带来了几个新的实验性功能。它现在还支持通过使用 TraceSectionMetric 进行基于自定义跟踪的时序测量,从而允许开发者针对特定的代码部分进行基准测试。此外,AudioUnderrunMetric 现在可以检测音频缓存欠载,以帮助开发者了解音频卡顿的情况。
BaselineProfileRule 可以生成配置文件来帮助进行运行时优化。它的工作方式与其他宏基准测试类似,您只需通过 lambda 代码表示用户操作即可。在下面的示例中,编译器应该提前优化的关键用户场景是冷启动: 从启动器打开应用的启动 Activity。
避免界面卡顿
全新的 JankStats 库可以帮您追踪和分析应用界面的性能问题,其中包括报告丢失渲染帧——通常被称为 "卡顿 (jank)"。JankStats 建立在现有 Android 平台 API(例如 FrameMetrics)之上,但最低可以用于 API Level 16。
JankStats 还提供了超越平台内置功能的其他能力:帮助定位丢帧原因的启发式算法、在报告中提供了额外上下文的界面状态,以及可以用于上传数据以进行分析的报告回调。
下面我们详细说说 JankStats 的三个主要功能:
识别卡顿 : JankStats 使用内置的启发式算法确定卡顿发生的时机,并使用该信息得知何时发布卡顿报告,从而使开发者可以获得有关这些问题的信息,以帮助分析和修复问题。
提供界面上下文 : 为了提高卡顿报告的可利用性和可操作性,JankStats 提供了一个帮助追踪当前界面和用户状态的机制。每当记录报告时,都会提供相应的信息,这样不但可以帮助开发者了解问题是何时发生的,更可以了解到用户当时在做什么。这有助于确定应用中存在问题的区域,以便稍后进行解决。这其中一些状态是由一些 Jetpack 库自动提供的,但我们也鼓励开发者提供自己应用特定的状态。
报告结果 : 在每一帧中,JankStats 客户端都会通过监听器收到包含该帧相关信息的通知,包括帧完成所用的时间、是否被视为卡顿,以及该帧显示期间的界面上下文是什么。我们鼓励客户端聚合和上传适合分析的数据,以帮助和调试整体性能问题。
界面库及指南
我们对界面库进行了一些更改,以更好地支持大屏幕兼容性、可折叠设备和 Emoji。
Jetpack Compose
Jetpack Compose 是 Android 用于构建原生界面的现代工具,如今已更新至 1.2 beta 版。新版本添加了一些用于支持先进用例的功能,包括支持可下载字体、惰性布局及嵌套滚动互操作性。更多信息请参阅文章:一起看 I/O | Jetpack Compose 中的新特性。
了解窗口状态
新的 WindowManager 库通过提供一个支持低至 API Level 14 的通用 API 界面,帮助开发人员适配他们的应用支持多窗口环境和新的设备形态。
最初的版本针对可折叠设备的用例,包括查询影响内容显示方式的物理属性。
Jetpack 的 SlidingPaneLayout 组件已更新为使用 WindowManager 的智能布局 API,以避免内容被放置于被遮挡区域 (例如跨越物理铰链区域)。
拖放
新的 DragAndDrop 通过让开发者接收来自应用内外的拖放数据,来帮助在新的外形和窗口模式下实现功能。DrapAndDrop 包含了一致的放置目标功能,它最低支持 API Level 24:
其他关键更新
Annotation
Annotation 库公开了元数据,从而帮助工具和其他开发者理解应用的代码。它提供了一些我们耳熟能详的注解,如 @NonNull。这些注解与 lint 检查配对,可以提高代码的正确性和可用性。
Annotation 正迁移至 Kotlin,所以正使用 Kotlin 的开发者会看到更合适的注解目标,包括 @file。
一些呼声很高的注解已随其相应的 lint 检查添加了进来。其中包括了有关方法或函数重写的注解,以及 @DeprecatedSinceApi 注解。后者作为 @RequiresApi 的必然结果,可以阻止在某个 API 级别之上进行使用。