Skip to content

Android 互操作

源:Kotlin for Android

Android 是 Kotlin Multiplatform 项目中最成熟的平台之一。理解如何在共享代码中集成 Android 特性,是构建跨平台应用的基础。

Kotlin 调用 Android API

kotlin
// androidMain
import android.content.Context
import android.content.SharedPreferences
import android.os.Build
import androidx.annotation.RequiresApi

// 访问 SharedPreferences
class AndroidStorage(private val context: Context) {
    private val prefs: SharedPreferences = context.getSharedPreferences(
        "app_prefs",
        Context.MODE_PRIVATE
    )
    
    fun saveString(key: String, value: String) {
        prefs.edit().putString(key, value).apply()
    }
    
    fun getString(key: String, default: String = ""): String {
        return prefs.getString(key, default) ?: default
    }
}

// 获取系统信息
fun getDeviceInfo(): String {
    return "Android ${Build.VERSION.RELEASE} (API ${Build.VERSION.SDK_INT})"
}

// 检查权限
fun hasPermission(context: Context, permission: String): Boolean {
    return context.checkSelfPermission(permission) == 
        android.content.pm.PackageManager.PERMISSION_GRANTED
}

// 获取资源
fun getStringResource(context: Context, resId: Int): String {
    return context.getString(resId)
}

Context 传递策略

方式 1:全局 Context 管理

kotlin
// androidMain
import android.app.Application

object AppContext {
    lateinit var application: Application
        private set
    
    fun init(app: Application) {
        application = app
    }
    
    val context: Context
        get() = application.applicationContext
}

// Application 类中初始化
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        AppContext.init(this)
    }
}
xml
<!-- AndroidManifest.xml -->
<application
    android:name=".MyApplication"
    ...>
</application>

方式 2:依赖注入传递

kotlin
// commonMain
interface StorageFactory {
    fun createStorage(): Storage
}

expect fun getStorageFactory(): StorageFactory

// androidMain
class AndroidStorageFactory(private val context: Context) : StorageFactory {
    override fun createStorage(): Storage {
        return AndroidStorage(context)
    }
}

actual fun getStorageFactory(): StorageFactory {
    return AndroidStorageFactory(AppContext.context)
}

方式 3:构造函数传递

kotlin
// commonMain
expect class PlatformContext

class UserRepository(context: PlatformContext) {
    // ...
}

// androidMain
actual typealias PlatformContext = Context

// Android 使用
val repository = UserRepository(context)

Android Jetpack 集成

ViewModel 集成

kotlin
// androidMain
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch

class UserViewModel(
    private val repository: UserRepository
) : ViewModel() {
    private val _users = MutableStateFlow<List<User>>(emptyList())
    val users: StateFlow<List<User>> = _users.asStateFlow()
    
    fun loadUsers() {
        viewModelScope.launch {
            _users.value = repository.getUsers()
        }
    }
}

LiveData 与 Flow 互操作

kotlin
// androidMain
import androidx.lifecycle.LiveData
import androidx.lifecycle.asLiveData
import kotlinx.coroutines.flow.Flow

// Flow → LiveData
fun <T> Flow<T>.toLiveData(): LiveData<T> {
    return this.asLiveData()
}

// 使用示例
class MyViewModel : ViewModel() {
    val users: LiveData<List<User>> = repository.observeUsers().toLiveData()
}

Compose 集成

kotlin
// androidMain
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember

@Composable
fun UserScreen(viewModel: UserViewModel) {
    val users by viewModel.users.collectAsState()
    
    LazyColumn {
        items(users) { user ->
            Text(text = user.name)
        }
    }
}

资源访问

字符串资源

kotlin
// androidMain
import android.content.res.Resources

class LocalizedStrings(private val resources: Resources) {
    fun get(resId: Int): String = resources.getString(resId)
    
    fun getFormatted(resId: Int, vararg args: Any): String {
        return resources.getString(resId, *args)
    }
}

// 从共享代码访问
expect class StringProvider {
    fun getString(key: String): String
}

// androidMain 实现
actual class StringProvider(private val context: Context) {
    actual fun getString(key: String): String {
        val resId = context.resources.getIdentifier(
            key,
            "string",
            context.packageName
        )
        return if (resId != 0) {
            context.getString(resId)
        } else {
            key // Fallback
        }
    }
}

Drawable 资源

kotlin
// androidMain
import android.graphics.drawable.Drawable
import androidx.core.content.ContextCompat

fun getDrawable(context: Context, resId: Int): Drawable? {
    return ContextCompat.getDrawable(context, resId)
}

// 图片加载
import coil.ImageLoader
import coil.request.ImageRequest

fun loadImage(context: Context, url: String, into: android.widget.ImageView) {
    val request = ImageRequest.Builder(context)
        .data(url)
        .target(into)
        .build()
    
    ImageLoader(context).enqueue(request)
}

权限处理

权限检查与请求

kotlin
// androidMain
import android.Manifest
import android.content.pm.PackageManager
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat

class PermissionManager(private val activity: ComponentActivity) {
    private val requestPermissionLauncher = activity.registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted ->
        // 处理权限结果
    }
    
    fun checkPermission(permission: String): Boolean {
        return ContextCompat.checkSelfPermission(
            activity,
            permission
        ) == PackageManager.PERMISSION_GRANTED
    }
    
    fun requestPermission(permission: String) {
        requestPermissionLauncher.launch(permission)
    }
}

// 在共享代码中抽象
interface PermissionHandler {
    suspend fun requestPermission(permission: String): Boolean
    fun hasPermission(permission: String): Boolean
}

生命周期感知

Lifecycle 集成

kotlin
// androidMain
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle

class DataObserver(private val lifecycleOwner: LifecycleOwner) {
    fun observe(flow: Flow<Data>, onData: (Data) -> Unit) {
        lifecycleOwner.lifecycleScope.launch {
            lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                flow.collect { data ->
                    onData(data)
                }
            }
        }
    }
}

WorkManager 集成

后台任务调度

kotlin
// androidMain
import androidx.work.*
import java.util.concurrent.TimeUnit

class DataSyncWorker(
    context: Context,
    params: WorkerParameters
) : CoroutineWorker(context, params) {
    
    override suspend fun doWork(): Result {
        return try {
            val repository = UserRepository(/* ... */)
            repository.syncData()
            Result.success()
        } catch (e: Exception) {
            Result.retry()
        }
    }
}

// 调度工作
fun scheduleDataSync(context: Context) {
    val constraints = Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .build()
    
    val syncRequest = PeriodicWorkRequestBuilder<DataSyncWorker>(
        15, TimeUnit.MINUTES
    )
        .setConstraints(constraints)
        .build()
    
    WorkManager.getInstance(context).enqueueUniquePeriodicWork(
        "data_sync",
        ExistingPeriodicWorkPolicy.KEEP,
        syncRequest
    )
}

Gradle 配置

Android 目标配置

kotlin
// shared/build.gradle.kts
plugins {
    alias(libs.plugins.kotlin.multiplatform)
    alias(libs.plugins.android.library)
}

kotlin {
    androidTarget {
        compilations.all {
            kotlinOptions {
                jvmTarget = "17"
            }
        }
    }
    
    sourceSets {
        androidMain.dependencies {
            implementation(libs.androidx.core.ktx)
            implementation(libs.androidx.lifecycle.viewmodel)
            implementation(libs.androidx.work.runtime)
        }
    }
}

android {
    namespace = "com.example.shared"
    compileSdk = 35
    
    defaultConfig {
        minSdk = 24
    }
    
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
}

ProGuard/R8 配置

proguard
# shared/proguard-rules.pro

# Kotlin 反射
-keep class kotlin.Metadata { *; }

# Kotlinx Serialization
-keepattributes *Annotation*, InnerClasses
-dontnote kotlinx.serialization.AnnotationsKt

# Coroutines
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}

# 共享模块类
-keep class com.example.shared.** { *; }

最佳实践

✅ 实践 1:最小化 Android 依赖

kotlin
// ✅ 在共享代码中定义接口
interface Analytics {
    fun logEvent(name: String, params: Map<String, Any>)
}

// ❌ 直接依赖 Android 库
import com.google.firebase.analytics.FirebaseAnalytics

✅ 实践 2:使用 Context 包装器

kotlin
// androidMain
class AppContextProvider(private val context: Context) {
    val filesDir: String
        get() = context.filesDir.absolutePath
    
    val cacheDir: String
        get() = context.cacheDir.absolutePath
    
    fun getSharedPreferences(name: String): SharedPreferences {
        return context.getSharedPreferences(name, Context.MODE_PRIVATE)
    }
}

✅ 实践 3:版本检查

kotlin
// androidMain
import android.os.Build
import androidx.annotation.RequiresApi

fun isAtLeastAndroid12(): Boolean {
    return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
}

@RequiresApi(Build.VERSION_CODES.S)
fun useAndroid12Feature() {
    // Android 12+ 特性
}

// 在运行时检查
if (isAtLeastAndroid12()) {
    useAndroid12Feature()
}

✅ 实践 4:测试友好的设计

kotlin
// 提供测试用的 Context 替代
interface AppContext {
    fun getString(resId: Int): String
    fun getSharedPreferences(name: String): SharedPreferences
}

class RealAppContext(private val context: Context) : AppContext {
    override fun getString(resId: Int) = context.getString(resId)
    override fun getSharedPreferences(name: String) = 
        context.getSharedPreferences(name, Context.MODE_PRIVATE)
}

class FakeAppContext : AppContext {
    override fun getString(resId: Int) = "test_string"
    override fun getSharedPreferences(name: String) = 
        /* Mock SharedPreferences */
}

常见集成库

依赖配置

toml
# gradle/libs.versions.toml
[versions]
androidx-core = "1.15.0"
androidx-lifecycle = "2.8.7"
androidx-work = "2.10.0"
androidx-room = "2.6.1"

[libraries]
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle" }
androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidx-lifecycle" }
androidx-work-runtime = { module = "androidx.work:work-runtime-ktx", version.ref = "androidx-work" }
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "androidx-room" }
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "androidx-room" }

Android 作为 Kotlin Multiplatform 的主力平台,提供了丰富的生态和工具链。合理利用 Android Jetpack 和现代 Android 开发模式,可以构建高质量的跨平台应用。