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

相关推荐
雨白1 小时前
重识 Java IO、NIO 与 OkIO
android·java
啦啦9117142 小时前
Niagara Launcher 全新Android桌面启动器!给手机换个门面!
android·智能手机
游戏开发爱好者82 小时前
iOS 上架要求全解析,App Store 审核标准、开发者准备事项与开心上架(Appuploader)跨平台免 Mac 实战指南
android·macos·ios·小程序·uni-app·iphone·webview
xrkhy2 小时前
canal1.1.8+mysql8.0+jdk17+redis的使用
android·redis·adb
00后程序员张2 小时前
混淆 iOS 类名与变量名的实战指南,多工具组合把混淆做成工程能力(混淆 iOS 类名变量名/IPA 成品混淆Ipa/Guard CLI 实操)
android·ios·小程序·https·uni-app·iphone·webview
介一安全4 小时前
【Frida Android】实战篇1:环境准备
android·网络安全·逆向·frida
许愿OvO4 小时前
MySQL触发器
android·mysql·adb
循环不息优化不止4 小时前
Jetpack Compose 从重组到副作用的全方位解析
android
2501_916007476 小时前
iOS文件管理工具深度剖析,从系统沙盒到跨平台文件操作的多工具协同实践
android·macos·ios·小程序·uni-app·cocoa·iphone
Android疑难杂症6 小时前
鸿蒙Notification Kit通知服务开发快速指南
android·前端·harmonyos