优先使用 KTX 库 | MAD Skills

在 Kotlin 中使用 Android 的 Java API 时,您会迅速意识到这样的做法失去了 Kotlin 语言简单有趣的特点。与其您亲自去编写这些 API 的 wrapper 和扩展函数,不如了解一下 Jetpack KTX 库。目前为止,已有超过 20 个库拥有对应的 KTX 版本,这些 KTX 库实现了常用的 Java 版本 API 的功能,包括 Android 平台 API、ViewModels、SQLite,甚至还有 Play Core。本文会介绍目前可用的 KTX API 并深入其中去分析它们是如何实现的。

如果您比较喜欢观看视频,可以 点击这里

可发现性

为了提高 ktx 功能的可发现性,作为最佳实践,当某个 ktx 库可用时,总是导入并使用它。由于 -ktx 传递依赖非 ktx 软件包,您不需要添加其他软件包。举个例子,使用 viewmodel 时,您可以看到两个软件包: viewmodel 和 viewmodel-ktx。-ktx 软件包会包含 Kotlin 的扩展:

// 获取最新 Lifecycle 库的版本信息 
// https://developer.android.google.cn/jetpack/androidx/releases/lifecycle
def lifecycle_version = "2.3.1"

// Java 实现
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"

// Kotlin 实现
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"

始终导入 -ktx 软件包

要使用 Android 平台 API 的 Kotlin 扩展,导入 core-ktx 软件包即可。

// 获取最新 Core 库的版本信息
// https://developer.android.google.cn/jetpack/androidx/releases/core3def corektx_version = "1.3.2"

implementation "androidx.core:core-ktx:$corektx_version"

大部分 ktx 功能使用 扩展函数 实现的,您可以通过 Android Studio 中的自动完成功能找到它们。

其他功能,像 Color 类上可以使用的 解构操作符重载 功能,可以访问 KTX 扩展程序列表 查看目前是否可以使用。

平台 API — core-ktx

core-ktx 为来自 Android 平台的 API 提供了常用的 Kotlin 功能。

例如,您正在使用 SharedPreferences,当您想去更新一个值,不需要像使用 Java 一样执行 3 个调用,您只需要执行一个调用即可:

/* Copyright 2020 Google LLC.  
   SPDX-License-Identifier: Apache-2.0 */

- val editor = sharedPreferences.edit()
- editor.putBoolean(SHOW_DELETED_WORDS_KEY, enable)
- editor.apply()
+ sharedPreferences.edit {
+ putBoolean(SHOW_DELETED_WORDS_KEY, enable)
+ }

在底层,ktx edit 方法和对应 Java 的 API 实现了相同的功能,但 ktx edit 函数 提供了一个更好的默认的数据提交 commit 选项: apply()。和 commit() 不同,apply() 函数会将数据修改异步写入磁盘。

 // 来源 https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:core/core-ktx/src/main/java/androidx/core/content/SharedPreferences.kt;l=39?q=SharedPreferences.kt 
 
 public inline fun SharedPreferences.edit(
     commit: Boolean = false,
     action: SharedPreferences.Editor.() -> Unit
 ) {
     val editor = edit()
     action(editor)
     if (commit) {
        editor.commit()
    } else {
        editor.apply()
    }
}

core-ktx 为处理平台常用的监听器提供了更加简单的方式。例如,您需要在 EditText 的 text 发生变化时触发一个操作,如果使用 Java,即使您只需要 onTextChanged(),您也必须实现 TextWatcher 接口中所有的函数。core-ktx 创建了 TextWatcher 中对应的方法: doOnTextChangeddoAfterTextChanged 以及 doBeforeTextChanged,在 Kotlin 中,您只需要实现您需要的接口:

 /* Copyright 2020 Google LLC.  
    SPDX-License-Identifier: Apache-2.0 */
 
- editWordView.addTextChangedListener(object : TextWatcher {
-     override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
-         handleTextChanged(s)
-     }
-
-     override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
-     }
-
-     override fun afterTextChanged(s: Editable) {
-     }
- })
+ editWordView.doOnTextChanged { text, start, count, after -> handleTextChanged(text) }

这样的变化会带来许多好处: 代码更加简洁,更好的命名和可空类型的注释,代码的可读性也得到提高。

AnimatorListenerTransitionListener 也有类似的 API。

实现原理上,doOnTextChanged 是 TextView 的扩展函数 – addTextChangedListener 也是 TextView 的扩展函数,doOnTextChanged 为其他 TextWatcher 的函数创建了 空实现

Jetpack API

可用的扩展主要提供给 Jetpack API 使用,这里我会快速介绍一下目前我使用的比较频繁的扩展。

LiveData

很多 LiveData 的功能都是作为扩展函数实现的,比如:

例如,使用 livedata-ktxmap 函数,我们不需要调用 Transformations.map(livedata) {/*map 函数体*/}livedata-ktx 允许我们通过 Kotlin 惯用的方式直接调用 livedData.map

当您观察一个 LiveData 对象时,您必须实现 Observer 接口。但是使用 lifecycle-ktx 的 observe 函数后,代码会变得更加简洁。如果提示 observe 方法找不到,请确认您已经导入 androidx.lifecycle.observe

/* Copyright 2020 Google LLC.  
    SPDX-License-Identifier: Apache-2.0 */
 
- wordViewModel.allWords.observe(
-     this,
-     Observer { words ->
-         // 更新 adapter 中缓存的 words 副本
-         words?.let { adapter.submitList(it) }
-     }
- )
+ wordViewModel.allWords.observe(owner = this) { words ->
+     // 更新 adapter 中缓存的 words 副本
+     words.let { adapter.submitList(it) }
+ }

LiveData 非常适合用于将数据暴露给 UI 使用,因此 lifecycle-livedata-ktx 软件包提供了两个简单的扩展函数: Flow.asLiveData()LiveData.asFlow(),分别对 Flow 转换成 LiveData 以及将 LiveData 转换成 Flow 予以支持。

Activity / Fragment 和 ViewModel

要构造一个 ViewModel,需要扩展 ViewModel 类,如果 ViewModel 有其他依赖,还需要实现 ViewModelProvider.Factory 接口。要实例化 ViewModel,可以使用 viewModels 委托 (详阅: Kotlin Vocabulary | Kotlin 委托代理): by ViewModels(factory):

/* Copyright 2020 Google LLC.  
   SPDX-License-Identifier: Apache-2.0 */

+ private val wordViewModel: WordViewModel by viewModels {
+     WordViewModelFactory(repository)
+ }
override fun onCreate(savedInstanceState: Bundle?) {
…
-     val viewModelFactory = WordViewModelFactory(repository)
-     val viewModel = ViewModelProvider(this, viewModelFactory).get(WordViewModel::class.java)
}

ActivityFragment 的 -ktx 软件包都提供了 viewModel 支持。

使用协程时,您可能会在 ViewModel 中启动一个协程。当 ViewModel 被销毁时,需要取消协程任务的执行。使用 viewModelScope 后,您不需要实现 CoroutineScope,协程任务的取消会在 viewModel.onCleared() 函数中自动执行。阅读 相关文章 了解 viewModelScope 的来龙去脉。

Room 和 WorkManager

Room 和 WorkManager 通过它们各自对应的 -ktx 软件包提供了对协程的支持。我们认为有必要更加深入地介绍这部分内容,请继续关注相应的 “Modern Android Development 技巧” (简称为 “MAD Skills”) 系列文章。

其他 KTX 模块

不仅仅是 AndroidX,其他一些模块也提供了对 KTX 的支持:

  • Firebase 创建了一些 通用 Kotlin 扩展
  • Google Maps 提供了 MapsPlaces 的 ktx 库;
  • Play Core 有 core-ktx 软件包,为监控应用内更新状态提供协程支持。

一旦您开始使用 -ktx 扩展,您的代码将会从简洁、易读和 Kotlin 习惯用语的特性中受益。敬请期待更多方法,并将 Kotlin 和 Jetpack 的优势应用于您的应用中。