Skip to content
RuaRuan
返回

Kotlin Multiplatform (KMP) 中,如何实现跨平台权限请求?

Kotlin Multiplatform (KMP) 中,如何实现跨平台权限请求?

在 Kotlin Multiplatform 中实现跨平台权限请求,最主流的方式是使用社区维护的第三方库,它们封装了底层的平台差异,能让你在共享代码中通过统一的 API 来请求权限。当然,也可以完全使用 Kotlin 原生的 expect/actual 机制自行封装。

目前,最常用的两个库是 kPermissionsmoko-permissions

特性kPermissionsmoko-permissions
特点API 设计现代、轻量,无需使用 Flow 或委托,支持声明式权限请求。历史悠久、社区成熟,功能强大,提供全面的权限类型和细致的异常处理。
Compose 支持原生支持,提供 rememberPermissionState 等 Compose 便捷方法。提供专门的 permissions-compose 模块,与 Compose 无缝集成。
模块化提供核心 API 和一系列功能模块,可按需添加。同样采用模块化设计,可按需引入特定权限模块。
优点API 直观,特别适合 Compose 项目,上手快。生态更成熟,支持权限类型丰富,异常处理机制完善。
缺点相对较新,社区规模和成熟度略逊于 moko。部分开发者认为其 API 稍显厚重。

对于 Compose Multiplatform 项目,kPermissions 的声明式 API 更契合现代 UI 开发模式。而如果你的项目对权限的精细控制要求很高,moko-permissions 会是更稳妥的选择。


🛠️ 两种主流实现方式

以下将分别介绍如何使用 kPermissionsmoko-permissions 以及 expect/actual 机制来实现跨平台权限请求。

📦 方式一:使用 kPermissions

kPermissions 库为 KMP + Compose 项目提供了非常便利的 API。

  1. 添加依赖 在你的 shared 模块的 build.gradle.kts 文件中,添加所需权限模块的依赖。

    // shared/build.gradle.kts
    kotlin {
        sourceSets {
            val commonMain by getting {
                dependencies {
                    // 例如,添加相机和图库权限
                    implementation("io.github.kpermissions:kpermissions-camera:1.0.1")
                    implementation("io.github.kpermissions:kpermissions-gallery:1.0.1")
                }
            }
        }
    }
  2. 使用 Compose API 请求权限 在共享 UI 中,你可以使用 rememberPermissionStateLaunchedEffect 以声明式方式触发权限请求。

    @Composable
    fun CameraScreen() {
        // 1. 创建权限状态
        val cameraPermission = rememberPermissionState(CameraPermission)
    
        // 2. 在副作用中触发权限请求
        LaunchedEffect(Unit) {
            cameraPermission.launchPermissionRequest { granted ->
                if (granted) {
                    // 权限已授予,可以打开相机
                } else {
                    // 权限被拒绝,可以展示说明或引导用户去设置中开启
                }
            }
        }
    
        // UI 界面
        // ...
    }

📦 方式二:使用 moko-permissions

moko-permissions 功能全面,同样支持 Compose,其 API 与 kPermissions 有所不同。

  1. 添加依赖 在你的 shared 模块的 build.gradle.kts 文件中添加核心依赖和 Compose 支持。

    // shared/build.gradle.kts
    kotlin {
        sourceSets {
            val commonMain by getting {
                dependencies {
                    // 核心权限 API
                    implementation("dev.icerock.moko:permissions:0.20.1")
                    // 权限模块,例如相机、位置等
                    implementation("dev.icerock.moko:permissions-camera:0.20.1")
                    implementation("dev.icerock.moko:permissions-location:0.20.1")
                    // 为 Compose Multiplatform 提供扩展
                    implementation("dev.icerock.moko:permissions-compose:0.20.1")
                }
            }
        }
    }
  2. 使用 PermissionsController 请求权限 moko-permissions 的核心是 PermissionsController。你需要在共享的 ViewModelPresenter 中持有它的实例。

    // 在共享的 ViewModel 中
    class MyViewModel(private val permissionsController: PermissionsController) {
        fun requestCameraPermission() {
            permissionsController.providePermission(Permission.CAMERA) { result ->
                when (result) {
                    is PermissionResult.Granted -> {
                        // 权限已授予
                    }
                    is PermissionResult.Denied -> {
                        // 权限被拒绝(非永久)
                        // 可以在此展示申请权限的缘由
                    }
                    is PermissionResult.DeniedAlways -> {
                        // 权限被永久拒绝
                        // 需要引导用户去系统设置中手动开启
                        permissionsController.openAppSettings()
                    }
                }
            }
        }
    }

🧱 方式三:使用 expect/actual 自行实现

如果不想引入第三方库,完全可以使用 Kotlin 的 expect/actual 机制来手动封装平台特定的权限 API。

  1. commonMain 中定义 expect 接口和状态 首先,定义通用的权限状态枚举和一个 expect 的权限处理器接口。

    // commonMain 代码
    enum class PermissionStatus { GRANTED, DENIED, PERMANENTLY_DENIED }
    
    expect class PlatformPermissionHandler() {
        suspend fun requestPermission(permission: String): PermissionStatus
        fun checkPermission(permission: String): PermissionStatus
    }
  2. androidMainiosMain 中提供 actual 实现 然后,分别针对 Android 和 iOS 平台提供这个接口的具体实现。

    • Android 实现 (androidMain)

      // androidMain 代码
      actual class PlatformPermissionHandler(
          private val activity: Activity
      ) {
          actual suspend fun requestPermission(permission: String): PermissionStatus {
              // 使用 Android 的 requestPermissions API 进行请求
              // ...
          }
          // ...
      }
    • iOS 实现 (iosMain)

      // iosMain 代码
      actual class PlatformPermissionHandler {
          actual suspend fun requestPermission(permission: String): PermissionStatus {
              return when (permission) {
                  "camera" -> requestCameraPermission()
                  // ...
                  else -> PermissionStatus.DENIED
              }
          }
      
          private suspend fun requestCameraPermission(): PermissionStatus {
              return suspendCancellableCoroutine { continuation ->
                  AVCaptureDevice.requestAccess(for: .video) { granted ->
                      continuation.resume(if (granted) PermissionStatus.GRANTED else PermissionStatus.DENIED)
                  }
              }
          }
          // ...
      }

📄 平台配置清单

在使用任何权限之前,必须确保在各个平台的工程中进行了正确的配置。这份清单务必逐项检查。

步骤AndroidiOS
1. 声明权限AndroidManifest.xml 文件中添加 <uses-permission> 标签。例如:<uses-permission android:name="android.permission.CAMERA" />Info.plist 文件中添加对应的 Usage Description 键值对。例如:<key>NSCameraUsageDescription</key><string>我们需要访问相机来拍照</string>
2. 请求时机在运行时请求”危险”权限(如相机、位置、存储)。权限请求均在运行时触发,系统会自动显示提示对话框。
3. 处理拒绝用户拒绝后,可再次请求,或通过 shouldShowRequestPermissionRationale 判断是否需要解释,最终可引导至系统设置开启。用户拒绝后,再次请求时系统通常不会弹出提示框。通常需要直接引导用户到系统设置中手动开启。

💎 总结与建议



上一篇
基于 CalDAV 的日程管理系统数据库设计
下一篇
Kotlinx Datetime 使用指南