别再自己写权限申请了!Android开发用EasyPermissions搞定相机、存储权限(附完整Kotlin代码)
告别繁琐权限申请用EasyPermissions优雅实现Android相机与存储权限管理每次开发需要相机或文件读写功能的应用时你是否也厌倦了重复编写那些模板化的权限申请代码从检查权限到处理拒绝逻辑再到引导用户跳转设置页这些看似简单的流程往往消耗开发者大量时间。本文将带你用EasyPermissions库彻底解决这一痛点通过Kotlin实战代码展示如何用1/10的代码量完成更健壮的权限管理。1. 原生权限申请的三大痛点在Android生态中权限管理经历了多次迭代从安装时授权到运行时动态申请开发者需要处理的边界条件越来越多。以下是原生API最让人头疼的几个问题不再询问陷阱当用户首次拒绝权限后勾选不再询问后续调用requestPermissions()将直接静默失败回调地狱需要在onRequestPermissionsResult中手动处理各种授权状态组合版本适配不同Android版本对存储权限的处理差异显著如Scoped Storage引入// 典型原生实现需要处理的复杂逻辑 override fun onRequestPermissionsResult( requestCode: Int, permissions: Arrayout String, grantResults: IntArray ) { when (requestCode) { PERMISSION_REQUEST_CODE - { val deniedPermissions mutableListOfString() grantResults.forEachIndexed { index, result - if (result ! PackageManager.PERMISSION_GRANTED) { deniedPermissions.add(permissions[index]) } } when { deniedPermissions.isEmpty() - { /* 全部授权 */ } shouldShowRequestPermissionRationale(deniedPermissions[0]) - { /* 显示解释对话框 */ } else - { /* 引导跳转系统设置 */ } } } } }2. EasyPermissions核心优势解析这个由Google开发者关系团队推荐的库主要解决了三个关键问题2.1 智能处理永久拒绝场景当检测到用户勾选不再询问时自动提供跳转系统设置的Dialog避免开发者重复实现这套逻辑。对比原生实现节省约80%的边界条件处理代码。2.2 链式调用与清晰回调将传统三段式流程检查→请求→处理简化为单一方法调用通过注解和接口分离成功/失败逻辑AfterPermissionGranted(RC_CAMERA_AND_STORAGE) private fun openCameraWithCheck() { if (EasyPermissions.hasPermissions(this, *REQUIRED_PERMS)) { startCamera() } else { EasyPermissions.requestPermissions( PermissionRequest.Builder( this, RC_CAMERA_AND_STORAGE, *REQUIRED_PERMS ) .setRationale(需要相机权限拍照和存储权限保存图片) .setPositiveButtonText(同意) .setNegativeButtonText(拒绝) .build() ) } }2.3 版本兼容层库内部自动处理Android各版本的权限差异特别是针对Android 10的Scoped Storage变更开发者无需再写版本判断分支。3. 完整集成指南Kotlin版3.1 基础配置在app模块的build.gradle中添加依赖dependencies { implementation pub.devrel:easypermissions:3.0.0 // 如需兼容Support库使用2.0.1 }声明所需权限AndroidManifest.xmluses-permission android:nameandroid.permission.CAMERA / !-- 适配Android 10的存储权限声明 -- uses-permission android:nameandroid.permission.READ_EXTERNAL_STORAGE android:maxSdkVersion28 / uses-permission android:nameandroid.permission.WRITE_EXTERNAL_STORAGE android:maxSdkVersion28 tools:ignoreScopedStorage /3.2 Activity/Fragment实现完整实现需要三个关键步骤实现PermissionCallbacks接口转发权限结果到EasyPermissions使用AfterPermissionGranted注解简化逻辑class CameraActivity : AppCompatActivity(), EasyPermissions.PermissionCallbacks { companion object { private const val RC_CAMERA_STORAGE 123 private val REQUIRED_PERMS arrayOf( Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE ) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_camera) btn_take_photo.setOnClickListener { openCameraWithCheck() } } AfterPermissionGranted(RC_CAMERA_STORAGE) private fun openCameraWithCheck() { when { EasyPermissions.hasPermissions(this, *REQUIRED_PERMS) - { // 已有权限直接操作 startCamera() } EasyPermissions.somePermissionPermanentlyDenied( this, REQUIRED_PERMS.toList() ) - { // 有权限被永久拒绝显示自定义引导 showPermissionDeniedDialog() } else - { // 首次申请或可再次请求 EasyPermissions.requestPermissions( PermissionRequest.Builder( this, RC_CAMERA_STORAGE, *REQUIRED_PERMS ) .setRationale(getString(R.string.permission_rationale)) .setPositiveButtonText(getString(R.string.btn_agree)) .setNegativeButtonText(getString(R.string.btn_deny)) .build() ) } } } override fun onRequestPermissionsResult( requestCode: Int, permissions: ArrayString, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) EasyPermissions.onRequestPermissionsResult( requestCode, permissions, grantResults, this ) } override fun onPermissionsGranted( requestCode: Int, perms: MutableListString ) { // 部分权限授予时的处理 if (requestCode RC_CAMERA_STORAGE) { if (perms.containsAll(REQUIRED_PERMS.toList())) { startCamera() } } } override fun onPermissionsDenied( requestCode: Int, perms: MutableListString ) { // 处理拒绝逻辑 if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) { AppSettingsDialog.Builder(this) .setTitle(需要权限) .setRationale(部分功能需要相机和存储权限请在设置中开启) .build() .show() } } private fun startCamera() { // 实际相机操作实现 } }3.3 高级配置技巧自定义拒绝对话框样式EasyPermissions.requestPermissions( PermissionRequest.Builder(this, RC_CODE, *perms) .setTheme(R.style.CustomPermissionDialog) .setRationale(我们需要这些权限来...) .setPositiveButtonText(好的) .setNegativeButtonText(取消) .build() )批量权限请求的最佳实践建议将权限按功能分组请求避免一次性弹出过多权限申请权限组使用场景请求时机相机存储拍照功能用户点击拍照按钮时位置地图功能进入地图页面时联系人分享功能点击分享按钮时4. 避坑指南与性能优化4.1 常见问题解决方案Q用户拒绝后如何优化再次请求体验A实现分时请求策略记录上次请求时间private fun shouldRequestAgain(): Boolean { val lastRequestTime prefs.getLong(KEY_LAST_REQUEST, 0) return System.currentTimeMillis() - lastRequestTime 24 * 60 * 60 * 1000 }Q如何适配Android 13的照片选择器A结合新的PhotoPickerAPI和权限检查fun selectPhotos() { if (Build.VERSION.SDK_INT Build.VERSION_CODES.TIRAMISU) { // 使用无需权限的PhotoPicker val intent Intent(MediaStore.ACTION_PICK_IMAGES) startActivityForResult(intent, REQ_PICK_IMAGE) } else { // 走传统权限检查流程 requestStoragePermission() } }4.2 性能优化建议延迟请求非必要权限不要在启动时集中请求上下文解释在触发具体功能前再请求对应权限异步检查将权限检查放在后台线程hasPermissions是线程安全的viewModelScope.launch(Dispatchers.IO) { val hasPermissions EasyPermissions.hasPermissions( context, *REQUIRED_PERMS ) withContext(Dispatchers.Main) { if (hasPermissions) { startFeature() } else { showPermissionRequest() } } }在实际项目中使用EasyPermissions后权限相关代码量平均减少70%特别是永久拒绝场景的处理变得异常简单。有个细节值得注意当使用AppSettingsDialog引导用户跳转设置后记得在onActivityResult中重新检查权限状态而不是假设用户一定会授予权限。