作者:fundroid

2021年Android开发新技术动向,未来的路该怎么走?

这篇文章偏阅读一些,大家可以了解下 的一些最新动向。

每年9/10月份 都会举行约为期2天的 Android Dev Summit,在活动上 Google 的技术专家们会分享 Android 的最新技术动向及开发心得。

 

今年 Summit 的 Slogan 是 “lent s,across devices” , 即使用 Jetpack 等 MAD Skill (Moden 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 来控制 OverScoll 的强度,当然你也可以通过 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或者 Custotivity 的方式自定义了开屏页,则需要进行适配,避免在 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 的WindoeMetrics 获取准确的窗口尺寸,再根据 izeClass 以最合适的布局显示当前 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 embeding

除了可以多窗口中打开多个应用,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。

  1. Android Auto 提供了针对驾驶员优化的应用体验,用户在 Android Auto 上创建连接手机的服务,手机应用可以以更优化的界面显示在车机上。
  2. Android Automotive OS 是一款基于 Android 的车载信息娱乐系统。车载系统是专为提升驾驶体验而优化的独立 Android 设备。相对于 Android Auto,它无需借助手机,用户可以将应用直接安装到车载系统上。

开发者可以跨平台的工程结构开发车机应用:

 

  1. car_app_common 是共享部分。
  2. 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.

KSP (Kotlin Symbol Procesing) 于9月份发布了 1.0 正式版。相比较于 KAPT 需要生成 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。

Mateiral3

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 @review

最近的 Andorid Studio 版本中对 Compose 的预览功能进行了多项强化:像原生视图那样,支持对 Compose UI 进行 3D 布局预览;对于一些字面值变量的修改无需重新编译即可实现预览的实时更新:

 

新增 Preview Configuration 面板,对 @Preview 注解中的参数修改更加快捷;

 

 

Jank Detection

在 Performance Profile 中新增了 Frames 视图,可以监控每一帧的耗时情况,更好地调试和发现 Jank 一类的问题。

 

此外,Android Studio 对模拟器进行了不少强化,模拟器模拟更多真实设备的使用场景,例如重力感应等。

八、AGP(Android Gradle Plugin)

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 开发上的优势已经逐渐凸显;
  • Flow 对 LiveData 以及 WorkManager 对 Service 替换的时机已经成熟;
  • ......

这些趋势将为我们未来的学习以及技术选型指明方向。

最后

在这里还分享一份由大佬亲自收录整理的学习PDF+架构视频+面试文档+源码笔记高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料

这些都是我现在闲暇时还会反复翻阅的精品资料。里面对近几年的大厂面试高频知识点都有详细的讲解。相信可以有效地帮助大家掌握知识、理解原理,帮助大家在未来取得一份不错的答卷。

当然,你也可以拿去查漏补缺,提升自身的竞争力。

真心希望可以帮助到大家,Android路漫漫,共勉!

胜象大百科