每年9/10月份 Google 都会举行为期约2天的 Android Dev Summit,在活动上 Google 的技术专家们会分享一些 Android 领域的技术动向以及开发心得。
今年 Summit 的 Slogan 是 “Excellent apps,across devices” , 即使用 Jetpack 等 MAD Skill (Modern Android Development) 开发出更优秀的应用,并通过 Android 系统落地到更多种类的智能设备。本次活动围绕这一主旨做了 30 多场技术分享(视频),涉及多个方向:
https://developer.android.com/events/dev-summit
- Android 12
- 12L
- Building across screens
- Kotlin
- Jetpack
- Jetpack Compose
- Android Studio
- AGP
Android 12
Material You
Android12 在10月进行了正式推送。Android12 的最大亮点就是基于 Material You 设计语言对原生系统 UI 进行了重新设计。 Material You 是 Material Design 的第3个版本,距离上一代 M2 已经过去了4年
跟上一代 M2 相比 M3 的元素面积更大、更便于用户点击;同时圆角的角度更大使得并排的元素之间的间隔更清晰。个性化是 M3 最大的特点,这也是 “You” 的命名来源。Android12 遵循了 M3 的 Dynamic Color 设计原则,系统可以从用户的壁纸中抓取颜色,然后色阶化应用到你开发的应用中,应用跟随主题的不同和变换颜色,千人千面。
Stretch OverScroll
https://developer.android.com/training/gestures/scroll#implement-stretch-overscroll
Android12 中加入了 Stretch OverScroll Effect ,相对于以前的水波纹效果,滚动反馈更加真实自然。开发者可以使用新增的 getDistance()
和 onPullDistance()
API 来控制 OverScroll 的强度,当然你也可以通过 XML 中设置 android:overScrollMode="never"
来屏蔽此效果。
App Splash Screen
https://developer.android.com/guide/topics/ui/splash-screen/migrate
Android12 增加了 Splash Screen API,可以在进入 App 主页之前自动插入开屏页,当然它的目的是为了让应用减少白屏的等待时间而非广告植入。Spash Screen 默认使用 App 的 Icon 作为开屏图案,开发者也可以使用系统提供的 API 自定义开屏图案甚至动画。如果在非 Android12 设备上也想使用Splash Screen功能,则可以使用 Jetpack 也提供了同名 SplashScreen 库,适配到了低至 Android 6(APP 23)的设备。需要注意,如果你的项目中通过 android:windowBackground 或者 CustomActivity 的方式自定义了开屏页,则需要进行适配,避免在 Android12 中出现两次开屏
Foreground service restrictions
https://developer.android.com/guide/components/foreground-services#background-start-restrictions
Android8 出于隐私保护的考虑,禁止了 Service 的后台启动,本次 Android12 中的限制进一步加强,除了一些特殊情况外,Foreground Service 也不允许在后台启动,否则会抛出 ForegroundServiceStartNotAllowedException
异常。 Service 的存在越来越鸡肋,或将逐渐被 WorkManager 所替代
Compatibility Test
每一个新版本的 Android 系统升级都会带来不少 API 的行为变动,Android12 也不例外。为了确保你的 APP 在这些变动下行为正常,一般需要修改 targetSDKVersion
进行针对性的测试。 Android11 起提供了兼容性测试工具,在不重新编译 APK 的情况下可以针对变动的 API 进行测试、提高测试效率。
在 Developer options > App compatibility changes
中可以找到测试工具
12L (Android 12 Large Screens)
https://developer.android.com/about/versions/12/12L
近年来,搭载 Android 系统的大屏设备增长迅速,除了平板类产品以外又出现了折叠屏手机这一新兴门类,目前已经有超过250万部大屏幕设备上运行着 Android 系统。为提高大屏设备的使用体验。 Android12 即将推出一个专门为大屏优化的版本,命名 12L。12L 针对大屏设备和折叠屏对界面进行了优化,例如当屏幕宽度大于 600dp 时将默认显示两列内容、引入了类似 Chrome OS 的 Dock 栏等,同时支持拖拽分屏等功能,同时在不同窗口中启动多个应用
WindowManager
为应对更多种类屏幕的出现,Jetpack 提供了 WindowManager 库,便于 App 更好地适配不同屏幕的尺寸。多窗口模式下的 App 不能再依赖 Display.getRealMetrics()
获取窗口尺寸,当屏幕状态变化导致,OnConfigurationChanged 发生时,使用 WindowManager 的 WindonMetrics
获取准确的窗口尺寸,再根据 WindowSizeClass
以最合适的布局显示当前 UI。
Jetpack Compose 能更好地以响应式的方式处理 OnConfigurationChanged
时的 UI 变化,非常适合配合在 12L 的设备上使用。
enum class WindowSizeClass { COMPACT, MEDIUM, EXPANDED }
@Composable
fun Activity.rememberWindowSizeClass() {
val configuration = LocalConfiguration.current
val windowMetrics = remember(configuration) {
WindowMetricsCalculator.getOrCreate()
.computeCurrentWindowMetrics(this)
}
val windowDpSize = with(LocalDensity.current) {
windowMetrics.bounds.toComposeRect().size.toDpSize()
}
val widthWindowSizeClass = when {
windowDpSize.width < 600.dp -> WindowSizeClass.COMPACT
windowDpSize.width < 840.dp -> WindowSizeClass.MEDIUM
else -> WindowSizeClass.EXPANDED
}
val heightWindowSizeClass = when {
windowDpSize.height < 480.dp -> WindowSizeClass.COMPACT
windowDpSize.height < 900.dp -> WindowSizeClass.MEDIUM
else -> WindowSizeClass.EXPANDED
}
// Use widthWindowSizeClass and heightWindowSizeClass
}
本次活动中分享的不少新技术都第一时间是赔了 Compose ,这也反映出 Android 将 Compose 作为首选的 UI 解决方案的决心。
Activity embedding
除了可以多窗口中打开多个应用,12L 还可以借助 XML 的配置或者调用 WindowManager 提供的 API 实现同一应用下多个 Activity 的并排显示。
Building across screens
Android Ware
Compose 技术栈采用了分层设计的思想,只要替换局部组件就可以迁移到不同平台中使用,例如 WareOs 中只需要替换 Material 和 Navigation 的便可以实现穿戴设备 UI 的开发。
以一个卡片为例,除了新增个别 Composable 以外,与手机端的写法别无二致
AppCard(
appImage = {
Image(painter = painterResource(id = R.drawable.ic_message), ... )
},
appName = { Text("Messages") },
time = { Text("12m") },
title = { Text("Kim Green") },
onClick = { ... },
content = {
Column(modifier = Modifier.fillMaxWidth()) {
Text("On my way!")
}
},
)
Android for Cars
Android 提供了两套车机系统 Android Auto 以及 Android Automotive OS。
- Android Auto 提供了针对驾驶员优化的应用体验,用户在 Android Auto 上创建连接手机的服务,手机应用可以以更优化的界面显示在车机上。
- Android Automotive OS 是一款基于 Android 的车载信息娱乐系统。车载系统是专为提升驾驶体验而优化的独立 Android 设备。相对于 Android Auto,它无需借助手机,用户可以将应用直接安装到车载系统上。
开发者可以跨平台的工程结构开发车机应用:
car_app_common
是共享部分automotive_os
和andorid_auto
是两个 build target
Kotlin
Kotlin Flow
https://medium.com/androiddevelopers/migrating-from-livedata-to-kotlins-flow-379292f419fb
Kotlin方面,本次活动上重点推荐了 Kotlin Flow 在 MVVM 架构中的应用。基于 Jetpack 的 lifecycle-ktx
扩展库 Flow 可以转变为一个 lifeycle-aware 组件,较好地替代现有的 LiveData 的使用场景。
你可以只在 Model 层使用 Flow,在 View 层仍然使用 LiveData,通过 Flow.asLiveData
将 Flow 转换为 LiveData:
// import androidx.lifecycle.asLiveData
class MessagesViewModel(repository: MessageRepository) : ViewModel() {
val userMessage = repository.userMessage.asLiveData()
...
}
当然 View 层也可以直接使用 Flow,在 lifecycleScope.launch { }
或 lifecycleScope.launchWheStart { }
中收集 Flow 的数据避免泄露,但是从性能出发更推荐使用 repeatOnLifecycle
:
//imprort androidx.lifecycle.repeatOnLifecycle
class MessagesActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.userMessages.collect { messages ->
listAdapter.submitList(messages)
}
}
}
}
}
当 MessagesActivity 离开 STARTED 时,协程及时取消节省资源。
此外使用 stateIn
可以将 Flow 转化为一个 StateFlow 以热流的形式确保数据的下游共享。 活动期间有网友在直播中询问是否还有 Flow 无法取代 LiveData 的场景,官方的回答是 LiveData 除了 API 更简单以外(相应的功能也比较弱),已经完全可以被 Flow 替代。
KSP
https://android-developers.googleblog.com/2021/09/accelerated-kotlin-build-times-with.html
KSP (Kotlin Symbol Processing) 于9月份发布了 1.0 正式版。相比较于 KAPT 需要生成 Java Stub 后再基于 APT 处理注解的流程,KSP 底层基于基于 Kotlin Compiler Plugin ,省去了 Java Stub 的生成,编译速度可以提高2倍以上,未来在 Kotlin Multiplatform Project 中也可使用,如果你的项目代码已经迁移到 Kotlin,那么未来的注解处理应该首选 KSP。
apply plugin: 'com.google.devtools.ksp'
dependencies {
...
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
ksp "androidx.room:room-compiler:$room_version"
}
将 KAPT 替换为 KSP 的配置非常简单,目前已经有包括 Room 在内的许多常见框架对 KSP 进行了支持,未来 Dagger,Hilt 等也将接入 KSP 以加速注解处理。
Jetpack
Room
https://medium.com/androiddevelopers/room-auto-migrations-d5370b0ca6eb
10月份 Room 发布 2.4.0 Beta 01,主要新增了 Auto Migratioins 和 Multi-map Relations 两个新 Features,同时支持使用 KSP 进行注解处理。
当数据库表结构发生变化时,需要通过数据库迁移保证数据的不丢失,例如字段名变化之类的变更,需要手写 SQL 才能完成升级,而基于 Auto Migrations 可以检测出两个表结构的区别,完成自动升级。
@Database(
version = MusicDatabase.LATEST_VERSION,
entities = { Song.class, Artist.class },
autoMigrations = {
@AutoMigration (
from = 1,
to = 2
)
},
exportSchema = true
)
public abstract class MusicDatabase extends RoomDatabase {
...
}
之前的版本中 Room 使用 @Relatioin
进行外键关联,为了避免多写 SQL 需要单独额外定义 Relatioin Class,其实对于 SQL 的态度没必要谈虎色变,适当地活用 SQL 有助于更简单地定义一对多的实体关系。
//Room Relations
data class ArtistAndSongs(
` @Embedded
val artist: Artist,
@Relation(...)
val songs: List<Song>
)
@Query("SELECT * FROM Artist")
fun getArtistAndSongs(): List<ArtistAndSongs>
//Room Multimap
@Query("SELECT * FROM Artist JOIN Song ON Artist.artistName = Song.songArtistName")
fun getAllArtistAndTheirSongsList(): Map<Artist, List<Song>>
Map<Artist, List<Song>>
这样的数据结构使用起来也更简单
WorkManager
https://medium.com/androiddevelopers/using-workmanager-on-android-12-f7d483ca0ecb
WorkManager 已经不单单是一个简单的异步任务处理框架,更是一整套强大的任务调度方案,可以有效替代 Service,更可靠地运行长时间的任务。最低可以向后兼容到 6.0,覆盖了市场绝多大数的机型。
WorkManager 2.6 支持 Multi-Process,借助 RemoteListenableWorker
或者 RemoteCoroutineWorker
可以将任务运行在任意指定进程,实现跨进程的监听;为应对 Android12 的 Foreground Service 的启动限制,WorkManager 2.7 新增了 setExpedited
API,可以高优的立即启动相关任务,不受后台启动的约束。
val request = OneTimeWorkRequestBuilder<HighPriorityWorker>()
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.build() WorkManager.getInstance(context).enqueue(request)
由于 CoroutineWorker.setForeground()
和 ListenableWorker.setForegroundAsync()
方法由 Foreground Service 提供支持,在一些禁止后台启动的场景中一旦被调用,会发生 ForegroundServiceStartNotAllowedException
异常,这是在开发中需要特别注意的。
More Components
此外,Jetpack 的其他一些库近期也都有新版本的发布。Navigation 2.4.0 beta 增加了多栈返回的支持,不同 NavHostFragment 的返回栈可以各自管理;DataStore 发布 1.0 可以更安全地替代 SharedPreferences 的使用;CameraX 1.1.0-alpha10 增加了 VideoCapture 视频截图和曝光补偿等实用功能; Benchmark 1.1.0-alpha11 增加了 Frame Timing,性能测试更加精准,并向后兼容到 API 23。
Jetpack Compose
Compose 新增 androidx.compose.material3
库,支持开发 Material You 主题风格的 UI。
Material3
Compose.M3 通过 ColorScheme 来自定义配色方案,支持了 Material You 的 color scheme 设计规范。
private val Blue40 = Color(0xff1e40ff)
private val DarkBlue40 = Color(0xff3e41f4)
private val Yellow40 = Color(0xff7d5700)
// Remaining colors from tonal palettes
private val LightColorScheme = lightColorScheme(
primary = Blue40,
secondary = DarkBlue40,
tertiary = Yellow40,
// error, primaryContainer, onSecondary, etc.
)
private val DarkColorScheme = darkColorScheme(
primary = Blue80,
secondary = DarkBlue80,
tertiary = Yellow80,
// error, primaryContainer, onSecondary, etc.
)
如上,定义 light 和 dark 两种 scheme,然后参数传递给 MaterialTheme
val darkTheme = isSystemInDarkTheme()
MaterialTheme(
colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme
) {
// M3 app content
}
Dynamic Color
Dynamic color 是 Material You 的最主要特色,在 Android12 及其后续设备可以通过设置 Dynamic ColoScheme
实现动态颜色切换:
// Dynamic color is available on Android 12+
val dynamicColor = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
val colorScheme = when {
dynamicColor && darkTheme -> dynamicDarkColorScheme(LocalContext.current)
dynamicColor && !darkTheme -> dynamicLightColorScheme(LocalContext.current)
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
如上,当应用了 Dynamic ColorScheme 后,选择红色或者蓝色墙纸后 App 的 UI 呈现对应的主题颜色
Android Studio
Android Studio Arctic Fox 正式版发布,Bumblebe 进入 Beta 阶段,而最新的 Canary 版本是 Chipmunk。 这近几个版本的迭代中 Android Studio 面向如何提高开发者的编码和调试效率增加了一系列新功能。
Compose @preview
最近的 Andorid Studio 版本中对 Compose 的预览功能进行了多项强化:像原生视图那样,支持对 Compose UI 进行 3D 布局预览;对于一些字面值变量的修改无需重新编译即可实现预览的实时更新:
新增 Preview Configuration 面板,对 @Preview 注解中的参数修改更加快捷;
Jank Detection
在 Performance Profile 中新增了 Frames 视图,可以监控每一帧的耗时情况,更好地调试和发现 Jank 一类的问题。
此外,Android Studio 对模拟器进行了不少强化,模拟器模拟更多真实设备的使用场景,例如重力感应等。
AGP
Non-transitive R class
AGP 7.0 以来针对编译速度的提升下了不少功夫,例如对 KSP 以及 Non-transitive R class 的支持。 Non-transitive R class 通过显示指定资源文件的完整包名,避免了 R 文件的隐式传递依赖、提升了编译速度,AGP 配合新的 Androi Studio 可以对工程进行 Non-transitive R class 的一键重构
Incremental Lint
AGP 7.0 引入 Lint 增量检查,大幅提升了 Lint 的检查速度
Configuration cache
https://medium.com/androiddevelopers/configuration-caching-deep-dive-bcb304698070
AGP 通过 Gradle 配置缓存的开启,可以显著提升各种情况下的编译速度
在 Android Studio 的 gradle.properties
中增加一下配置即可启动 Configuration Cache
org.gradle.unsafe.configuration-cache=true
Summary
Android Dev Summit 的分享主题涉及了 Android 领域的方方面面,开发者无需了解,更重要的是从这些分享中洞察到未来的技术的发展趋势,比如未来的 App 可能需要适配更多而屏幕尺寸、Jetpack Compose 在 UI 开发上的先进性正逐渐凸显;Kotlin Flow 对 LiveData 以及 WorkManager 对 Service 的替代趋势也逐渐清晰。。