每个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 的发展日新月异 ------ 新的编程模式和工具不断涌现。

相关推荐
lar_slw2 小时前
flutter json转实体类
android·flutter·json
用户2018792831673 小时前
轻松理解Ashmem实现原理
android
苏世-顾长歌4 小时前
Android studio导入OpenCV报“Unresolved reference: android“
android·opencv·android studio
qq_252924194 小时前
PHP 8.0+ 高级特性深度探索:架构设计与性能优化
android·性能优化·php
恋猫de小郭4 小时前
基于 Dart 的 Terminal UI ,pixel_prompt 这个 TUI 库了解下
android·前端·flutter
怪兽20145 小时前
谈一谈Java成员变量,局部变量和静态变量的创建和回收时机
android·面试
usabcd26 小时前
如何重新编译HyperLPR原生库以消除16k对齐警告
android·c++·cmake·ndk·mnn·16k对齐·hyperlpr
Joan_Vivian6 小时前
旧项目适配Android15
android·java
灿烂阳光g6 小时前
Android启动优化
android