每个Kotlin开发者应该掌握的最佳实践,第三趴

在该系列的 第一篇第二篇 中,我们探讨了 Kotlin 的基础知识、高级语言特性,以及编写简洁、可维护代码的模式。

在这篇文章中,我们将转向实际开发实践,包括测试、性能优化、架构以及 Kotlin 多平台(KMP)。

这些实践对于可投入生产的应用程序和大型代码库至关重要。

使用协程编写单元测试

Kotlin 协程和 Flow 很棒,但它们需要进行恰当的测试。使用runTest(来自kotlinx-coroutines -test)来确定性地测试挂起函数和数据流。

Kotlin 复制代码
@Test
fun testFlowEmitsValues() = runTest {
    val flow = flowOf(1, 2, 3)
    val results = flow.toList()
    assertEquals(listOf(1, 2, 3), results)
}

最佳实践:始终将业务逻辑放在 ViewModel/用例层中,使其在不依赖安卓的情况下也能进行测试。

避免过度设计

这条规则不仅仅适用于 Kotlin,甚至所有的项目或者语言都适用。

虽然 MVVMMVI 和整洁架构很受欢迎,但除非有需要,否则不要增加复杂性。

  • 对于原型或演示应用 -> 一个 Activity 的结构够用了。
  • 对于小型应用程序 -> MVVM 就足够了。
  • 对于大型应用程序 -> 可以考虑整洁架构,但要务实一些。
  • 避免不必要的深层级(例如,一个只有 3 个屏幕的应用程序设置 存储库->用例->管理器->服务->数据访问对象 这样的层级架构,这会显著增加维护成本,得不偿失。)。

Compose 中的性能优化

Jetpack Compose 是声明式的,但使用不当会影响性能。

  • 使用 remember 来避免重新组合。
  • 提升状态------将状态保存在 ViewModel 或父级可组合项中,而不是放在 UI 的深层。
  • 使用 LazyColumn 而不是手动创建可滚动列表。
  • 尽可能使用不可变集合。
Kotlin 复制代码
val counter = remember { mutableStateOf(0) }
Button(onClick = { counter.value++ }) {
    Text("Clicks: ${counter.value}")
}

状态管理中优先使用不可变性

不可变状态有助于避免棘手的 UI 错误。

  • 用密封类或数据类来表示用户界面状态。
  • 避免直接从 ViewModel 中暴露可变对象。
Kotlin 复制代码
data class UiState(val isLoading: Boolean, val data: List<String> = emptyList())

实际示例:运用最佳实践加载用户数据

让我们在一个实际场景中结合密封的用户界面状态(UiState)、数据流(Flow)、Compose 和单元测试。

密封的用户界面状态

Kotlin 复制代码
sealed class UserUiState {
    object Loading : UserUiState()
    data class Success(val users: List<String>) : UserUiState()
    data class Error(val message: String) : UserUiState()
}

在 ViewModel 中使用 Flow

Kotlin 复制代码
class UserViewModel(
    private val repository: UserRepository
) : ViewModel() {

    private val _uiState = MutableStateFlow<UserUiState>(UserUiState.Loading)
    val uiState: StateFlow<UserUiState> = _uiState
    
    init { loadUsers() }
    
    private fun loadUsers() {
        viewModelScope.launch {
            try {
                val users = repository.getUsers()
                _uiState.value = UserUiState.Success(users)
            } catch (e: Exception) {
                _uiState.value = UserUiState.Error("Failed to load users")
            }
        }
    }
}

存储

Kotlin 复制代码
class UserRepository {
    suspend fun getUsers(): List<String> {
        delay(1000) // 模拟网络请求
        return listOf("Alice", "Bob", "Charlie", "John")
    }
}

Compose UI

Kotlin 复制代码
@Composable
fun UserScreen(viewModel: UserViewModel = viewModel()) {
    val state by viewModel.uiState.collectAsState()
    when (state) {
        is UserUiState.Loading -> CircularProgressIndicator()
        is UserUiState.Success -> {
            val users = (state as UserUiState.Success).users
            LazyColumn {
                items(users) { user ->
                    Text(user, modifier = Modifier.padding(16.dp))
                }
            }
        }
        is UserUiState.Error -> {
            Text(
                text = (state as UserUiState.Error).message,
                color = Color.Red,
                modifier = Modifier.padding(16.dp)
            )
        }
    }
}

单元测试

Kotlin 复制代码
@OptIn(ExperimentalCoroutinesApi::class)
class UserViewModelTest {
    private val testDispatcher = StandardTestDispatcher()
    
    @Before fun setup() { Dispatchers.setMain(testDispatcher) }
    @After fun tearDown() { Dispatchers.resetMain() }
    
    @Test
    fun testLoadUsers_success() = runTest {
        val repository = UserRepository()
        val viewModel = UserViewModel(repository)
        val state = viewModel.uiState.value
        assertTrue(state is UserUiState.Success)
        assertEquals(listOf("Alice", "Bob", "Charlie", "John"), (state as UserUiState.Success).users)
    }
}

此示例展示了:

  • 用于不可变状态的密封类。
  • ViewModel 中的数据流(Flow)和协程。
  • 对状态做出响应的 Jetpack Compose 用户界面。
  • 使用 runTest
  • 和协程实现的可测试性。

合理使用依赖注入(DI)

依赖注入可提高可测试性和模块化程度。在安卓开发中,推荐使用 Hilt (如果你想要更轻量级的替代方案,也可以选择 Koin)。

最佳实践:避免过度注入。仅注入需要共享的内容(例如存储库、API 客户端)。

最小化 APK/应用包大小

  • 启用 R8ProGuard
  • shrinkResources 设置为 true 以移除未使用的资源。
  • 使用 const val 定义键,而不是使用大型配置对象。
  • 对于重量级对象,优先使用 lazy {} 进行初始化。

Kotlin 多平台(KMP)最佳实践

2025 年,Kotlin 多平台技术发展迅速。为保证代码整洁,需做到:

  • 共享业务逻辑(模型、仓储、用例)。
  • 保持用户界面特定于平台(安卓使用 Jetpack Compose,iOS 使用 SwiftUI)。
  • 网络通信使用 Ktor ,共享数据库使用 SQLDelight ,序列化处理使用 kotlinx-serialization

文档使用 KDoc

优质的文档能避免未来的麻烦。对公共函数、类和模块使用 KDoc(/** ... */)。

Kotlin 复制代码
/**
 * Fetches user profile data from the server.
 *
 * @param userId ID of the user to fetch.
 * @return A [User] object containing profile information.
 */
suspend fun getUserProfile(userId: String): User

KDoc 是 Kotlin 的标准文档注释格式,编写方式以 Markdown 语法为主。

静态代码分析

使用以下工具:

  • ktlint -> 代码格式化
  • detekt -> 检测代码异味和复杂度
  • SonarQube -> 评估可维护性指标

最佳实践:将这些工具集成到持续集成/持续交付(CI/CD)管道中。

时刻考虑可扩展性

在使用 Kotlin 进行编码时,要问自己:

  • 这段代码会随着应用程序的发展而具备可扩展性吗?
  • 它是否可测试且易于维护?
  • 新团队成员能否轻松理解它?

一些小的决策(比如命名、分层和不可变性)日后会累积带来巨大的生产力提升。

总结

通过第三部分的内容,我们涵盖了测试、架构、性能、Kotlin 多平台编程(KMP)以及代码质量等方面。到现在,你应该不仅对编写 Kotlin 代码有了扎实的掌握,更能够编写符合生产标准的 Kotlin 代码。

但这仅仅是个开始。Kotlin 的发展日新月异 ------ 新的编程模式和工具不断涌现。

相关推荐
阿巴斯甜14 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker14 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952715 小时前
Andorid Google 登录接入文档
android
黄林晴17 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android