前置知识: Kotlin

Kotlin与Android

3 minIntermediate2026/6/14

Kotlin Android开发

概述

Kotlin 是 Android 官方推荐的开发语言。自 2019 年 Google 宣布 Kotlin 为 Android 首选语言以来,几乎所有新项目都使用 Kotlin 开发。Kotlin 的空安全、协程、扩展函数等特性,让 Android 开发更加安全和高效。

本文介绍 Kotlin 在 Android 开发中的核心用法,包括 Activity、ViewModel、生命周期、常用模式等。

基础概念

  • Activity:Android 应用的单个屏幕,用户交互的入口
  • Fragment:Activity 中的模块化 UI 片段,可复用
  • ViewModel:存储和管理 UI 相关的数据,在配置变更时保留
  • Lifecycle:Android 组件的生命周期,ViewModel 感知生命周期来避免内存泄漏
  • Intent:Android 组件之间通信的消息对象
  • Context:Android 环境的上下文对象,访问资源和系统服务

快速上手

一个最简单的 Android Activity:

// MainActivity.kt
package com.example.myapp

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.material3.*
import androidx.compose.runtime.*

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 找到视图并设置点击事件
        val button = findViewById<Button>(R.id.my_button)
        button.setOnClickListener {
            Toast.makeText(this, "按钮被点击", Toast.LENGTH_SHORT).show()
        }
    }
}

使用 Compose 的方式:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MaterialTheme {
                GreetingScreen()
            }
        }
    }
}

@Composable
fun GreetingScreen() {
    var count by remember { mutableStateOf(0) }
    Column(modifier = Modifier.padding(16.dp)) {
        Text("点击次数: $count")
        Button(onClick = { count++ }) {
            Text("点击我")
        }
    }
}

详细用法

ViewModel 和 StateFlow

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch

// 定义 UI 状态
data class UserUiState(
    val isLoading: Boolean = false,
    val user: User? = null,
    val error: String? = null
)

class UserViewModel(private val repository: UserRepository) : ViewModel() {
    // 使用 StateFlow 管理 UI 状态
    private val _uiState = MutableStateFlow(UserUiState())
    val uiState: StateFlow<UserUiState> = _uiState.asStateFlow()

    // 加载用户数据
    fun loadUser(userId: String) {
        viewModelScope.launch {
            _uiState.update { it.copy(isLoading = true) }
            try {
                val user = repository.getUser(userId)
                _uiState.update { it.copy(isLoading = false, user = user) }
            } catch (e: Exception) {
                _uiState.update { it.copy(isLoading = false, error = e.message) }
            }
        }
    }
}

在 Activity/Fragment 中观察 ViewModel:

class UserActivity : AppCompatActivity() {
    // 创建 ViewModel
    private val viewModel: UserViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // 观察 UI 状态
        lifecycleScope.launch {
            viewModel.uiState.collect { state ->
                when {
                    state.isLoading -> showLoading()
                    state.error != null -> showError(state.error)
                    state.user != null -> showUser(state.user)
                }
            }
        }

        // 触发数据加载
        viewModel.loadUser("1")
    }
}

协程与生命周期

import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.*

class MyActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // lifecycleScope:Activity 销毁时自动取消
        lifecycleScope.launch {
            // 在主线程启动
            val data = withContext(Dispatchers.IO) {
                // 在 IO 线程执行网络请求
                apiService.fetchData()
            }
            // 自动回到主线程更新 UI
            updateUI(data)
        }

        // Lifecycle 感知的协程
        lifecycleScope.launchWhenStarted {
            // 只有在 Started 状态时才执行
            val result = repository.loadData()
            updateUI(result)
        }
    }
}

Intent 和导航

// 启动另一个 Activity
fun navigateToDetail(userId: String) {
    val intent = Intent(this, DetailActivity::class.java).apply {
        putExtra("USER_ID", userId)
    }
    startActivity(intent)
}

// 在目标 Activity 中获取参数
class DetailActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val userId = intent.getStringExtra("USER_ID") ?: return
        // 使用 userId 加载数据
    }
}

// 使用 Navigation Compose
@Composable
fun NavGraph() {
    val navController = rememberNavController()
    NavHost(navController, startDestination = "home") {
        composable("home") {
            HomeScreen(
                onUserClick = { id ->
                    navController.navigate("detail/$id")
                }
            )
        }
        composable(
            "detail/{userId}",
            arguments = listOf(navArgument("userId") { type = NavType.StringType })
        ) { backStackEntry ->
            val userId = backStackEntry.arguments?.getString("userId") ?: ""
            DetailScreen(userId)
        }
    }
}

权限请求

import android.Manifest
import androidx.activity.result.contract.ActivityResultContracts

class CameraActivity : AppCompatActivity() {
    // 注册权限请求
    private val requestPermission = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted ->
        if (isGranted) {
            openCamera()
        } else {
            Toast.makeText(this, "需要相机权限", Toast.LENGTH_SHORT).show()
        }
    }

    fun checkAndRequestCameraPermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
            == PackageManager.PERMISSION_GRANTED
        ) {
            openCamera()
        } else {
            requestPermission.launch(Manifest.permission.CAMERA)
        }
    }
}

常见场景

网络请求与错误处理

class NewsViewModel(private val repository: NewsRepository) : ViewModel() {
    private val _news = MutableStateFlow<List<News>>(emptyList())
    val news: StateFlow<List<News>> = _news.asStateFlow()

    fun loadNews() {
        viewModelScope.launch {
            try {
                val result = repository.getNews()
                _news.value = result
            } catch (e: HttpException) {
                // 服务器错误
            } catch (e: IOException) {
                // 网络错误
            }
        }
    }
}

本地数据存储

import android.content.Context
import androidx.datastore.preferences.*

// 使用 DataStore 存储简单数据
val Context.dataStore by preferencesDataStore(name = "settings")

class SettingsManager(private val context: Context) {
    // 定义键
    private object Keys {
        val DARK_MODE = booleanPreferencesKey("dark_mode")
        val FONT_SIZE = intPreferencesKey("font_size")
    }

    // 读取设置
    val darkMode: Flow<Boolean> = context.dataStore.data.map { preferences ->
        preferences[Keys.DARK_MODE] ?: false
    }

    // 保存设置
    suspend fun setDarkMode(enabled: Boolean) {
        context.dataStore.edit { preferences ->
            preferences[Keys.DARK_MODE] = enabled
        }
    }
}

通知

import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat

fun showNotification(context: Context, title: String, message: String) {
    val notification = NotificationCompat.Builder(context, "channel_id")
        .setSmallIcon(R.drawable.ic_notification)
        .setContentTitle(title)
        .setContentText(message)
        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        .build()

    NotificationManagerCompat.from(context).notify(1, notification)
}

注意事项

  • 不要在 ViewModel 中持有 Activity 引用:这会导致内存泄漏,使用 AndroidViewModel 的 applicationContext 如果确实需要 Context
  • 主线程不能做耗时操作:网络请求、数据库操作等必须在 IO 线程
  • 生命周期感知:使用 lifecycleScope 或 viewModelScope,确保协程在组件销毁时自动取消
  • 字符串资源:不要硬编码字符串,使用 getString(R.string.xxx)
  • 避免在 onDraw 中创建对象:自定义 View 的 onDraw 方法会被频繁调用,在其中创建对象会导致 GC 压力

进阶用法

Hilt 依赖注入

// Application 类
@HiltAndroidApp
class MyApp : Application()

// Activity 注入
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject lateinit var repository: UserRepository

    private val viewModel: UserViewModel by viewModels()
}

// Module 提供依赖
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
    @Provides
    @Singleton
    fun provideUserRepository(api: ApiClient): UserRepository {
        return UserRepositoryImpl(api)
    }
}

WorkManager 后台任务

import androidx.work.*

class SyncWorker(appContext: Context, params: WorkerParameters) :
    CoroutineWorker(appContext, params) {

    override suspend fun doWork(): Result {
        return try {
            // 执行后台同步任务
            repository.syncData()
            Result.success()
        } catch (e: Exception) {
            Result.retry()
        }
    }
}

// 调度任务
fun scheduleSync(context: Context) {
    val request = PeriodicWorkRequestBuilder<SyncWorker>(15, TimeUnit.MINUTES)
        .setConstraints(Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .build())
        .build()

    WorkManager.getInstance(context).enqueueUniquePeriodicWork(
        "sync",
        ExistingPeriodicWorkPolicy.KEEP,
        request
    )
}