
PermissionsDispatcher 的 Java 使用指南注解驱动的 Android 运行时权限处理完整实践【免费下载链接】PermissionsDispatcherA declarative API to handle Android runtime permissions.项目地址: https://gitcode.com/gh_mirrors/pe/PermissionsDispatcher导读本文以 PermissionsDispatcher 官方 Java 文档doc/java_usage.md为主体完整讲解如何在纯 Java 项目中用声明式注解替代繁琐的checkSelfPermission/requestPermissions样板代码。你将掌握 5 个核心注解的语义、注解处理器自动生成的XxxPermissionsDispatcher类的委托方式以及底层生成代码的真实结构并最终能在自己的 Activity / Fragment 中落地一套完整、可运行、经测试验证的运行时权限方案。一、先了解 PermissionsDispatcher 的定位PermissionsDispatcher 是一个基于注解处理annotation processing的运行时权限库你在源码里用少量注解声明哪个方法需要什么权限、权限被拒时做什么编译期处理器会自动生成一个辅助类把权限检查、请求发起、结果回调、rationale 弹窗等逻辑全部接管。项目 README 明确给出三条特性完全支持 Kotlin / Java、支持特殊权限Special Permissions、100% 无反射见 README.md。Java 侧走annotationProcessor路径即本文主题。二、零、准备 AndroidManifest声明权限无论使用哪个运行时权限库第一步都是先在AndroidManifest.xml中声明目标权限uses-permission android:nameandroid.permission.CAMERA /运行时权限dangerous permission必须先在 Manifest 中声明代码中才能请求。本文示例以相机权限Manifest.permission.CAMERA贯穿始终若使用联系人或特殊权限照葫芦画瓢替换权限名即可。三、一、为类与方法挂上注解PermissionsDispatcher 只引入少量注解API 保持精简。下表是官方文档的注解总览含各注解的源码定位AnnotationRequiredDescription源码位置RuntimePermissions✓在类上注册一个Activity或Fragment声明其需要被权限框架接管annotation/.../RuntimePermissions.javaNeedsPermission✓标注真正执行需要权限的操作的方法可指定一个或多个权限annotation/.../NeedsPermission.javaOnShowRationale标注解释为什么需要该权限的方法接收一个PermissionRequest对象用于在用户输入后继续或中止请求annotation/.../OnShowRationale.javaOnPermissionDenied用户未授予权限时调用的方法annotation/.../OnPermissionDenied.javaOnNeverAskAgain用户勾选不再询问时调用的方法annotation/.../OnNeverAskAgain.java注意官方文档特别强调——被注解的方法不能是private。这是注解处理器校验规则的一部分处理器中还有PrivateMethodException等一整套校验异常见 processor/.../exception/PrivateMethodException.kt。下面是一个完整的最小示例MainActivity需要Manifest.permission.CAMERARuntimePermissions public class MainActivity extends AppCompatActivity { NeedsPermission(Manifest.permission.CAMERA) void showCamera() { getSupportFragmentManager().beginTransaction() .replace(R.id.sample_content_fragment, CameraPreviewFragment.newInstance()) .addToBackStack(camera) .commitAllowingStateLoss(); } OnShowRationale(Manifest.permission.CAMERA) void showRationaleForCamera(final PermissionRequest request) { new AlertDialog.Builder(this) .setMessage(R.string.permission_camera_rationale) .setPositiveButton(R.string.button_allow, (dialog, button) - request.proceed()) .setNegativeButton(R.string.button_deny, (dialog, button) - request.cancel()) .show(); } OnPermissionDenied(Manifest.permission.CAMERA) void showDeniedForCamera() { Toast.makeText(this, R.string.permission_camera_denied, Toast.LENGTH_SHORT).show(); } OnNeverAskAgain(Manifest.permission.CAMERA) void showNeverAskForCamera() { Toast.makeText(this, R.string.permission_camera_neverask, Toast.LENGTH_SHORT).show(); } }关键点拆解NeedsPermission(Manifest.permission.CAMERA)NeedsPermission的value()是String[]类型见 NeedsPermission.java因此可以一次声明多个权限例如NeedsPermission(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)——sample 中的showContacts()正是这么写的见 sample/.../MainActivity.kt。OnShowRationale与PermissionRequestrationale 方法接收一个PermissionRequest参数。PermissionRequest只有两个方法proceed()与cancel()见 annotation/.../PermissionRequest.java分别表示用户同意继续请求与用户拒绝。官方文档补充如果 rationale 方法不指定参数编译器会生成process${NeedsPermissionMethodName}ProcessRequest与cancel${NeedsPermissionMethodName}ProcessRequest两个方法可替代PermissionRequest使用例如配合DialogFragment的场景。关联注解的权限列表必须一致OnShowRationale/OnPermissionDenied/OnNeverAskAgain的value()必须与对应的NeedsPermission完全一致。处理器通过findMatchingMethodForNeeds按权限值精确配对见 processor/.../util/Helpers.kt配不上会直接编译失败。四、二、把权限处理委托给生成类编译后注解处理器会为MainActivity生成一个名为MainActivityPermissionsDispatcher的类命名规则为[Activity Name] PermissionsDispatcher对应常量GEN_CLASS_SUFFIX PermissionsDispatcher见 processor/.../util/Constants.kt。你唯一要做的就是把这个 helper 类接进生命周期回调Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.button_camera).setOnClickListener(v - { // NOTE: delegate the permission handling to generated method MainActivityPermissionsDispatcher.showCameraWithPermissionCheck(this); }); } Override public void onRequestPermissionsResult(int requestCode, NonNull String[] permissions, NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); // NOTE: delegate the permission handling to generated method MainActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults); }两处委托的含义入口showCameraWithPermissionCheck(this)是生成方法后缀常量GEN_WITH_PERMISSION_CHECK_SUFFIX WithPermissionCheck。它内部先做权限状态检查已授权 → 直接调用你的showCamera()未授权 → 按需走 rationale 或直接发起系统请求。出口在onRequestPermissionsResult中把回调转交给MainActivityPermissionsDispatcher.onRequestPermissionsResult(...)由生成代码根据 requestCode 匹配到对应请求再根据授权结果分发到showCamera()/showDeniedForCamera()/showNeverAskForCamera()。关于 requestCode生成类里每个NeedsPermission方法都有一个专属的REQUEST_XXX静态常量字段由RequestCodeProvider用AtomicInteger原子递增产生保证整个应用内唯一见 processor/.../RequestCodeProvider.kt。你在onRequestPermissionsResult里无需关心具体数值框架会自己匹配。五、深入原理生成代码长什么样虽然生成代码在编译期才产出但从处理器源码可以精确还原它的结构。JavaBaseProcessorUnit.createTypeSpec显示生成的类包含见 processor/.../impl/java/JavaBaseProcessorUnit.kt静态常量字段每个注解方法对应REQUEST_XXX请求码、PERMISSION_XXX权限字符串数组、必要时还有PENDING_XXX挂起的GrantableRequest以及带参数方法所需的参数缓存字段私有构造器类不可实例化全部以静态方法对外createWithPermissionCheckMethods生成xxxWithPermissionCheck(target)系列入口方法createOnShowRationaleCallbackMethods生成 rationale 回调链createPermissionHandlingMethods生成onRequestPermissionsResult分发逻辑createPermissionRequestClasses为每个带PermissionRequest参数的方法生成内部XxxPermissionRequest类。针对 ActivityJavaActivityProcessorUnit揭示了实际调用链见 processor/.../impl/java/JavaActivityProcessorUnit.kt权限状态判断使用androidx.core.app.ActivityCompat/ContextCompat体系rationale 判断调用ActivityCompat.shouldShowRequestPermissionRationale(target, permission)发起请求调用ActivityCompat.requestPermissions(target, permission, requestCode)。也就是说生成代码内部完全基于 AndroidX 的ActivityCompat实现这正是 README 所述4.x 仅支持 Jetpack的原因使用 appcompat 的旧项目需要停留在 3.x详见 README.md 与 doc/migration_guide.md。另外JavaBaseProcessorUnit中还有一张特殊权限映射表把android.permission.WRITE_SETTINGS与android.permission.SYSTEM_ALERT_WINDOW分别路由到WriteSettingsHelper/SystemAlertWindowHelper见 JavaBaseProcessorUnit.kt。这两类权限不走requestPermissions而是打开系统设置页让用户手动授权框架会生成不同的 helper 处理。详细用法见 doc/special_permissions.md。六、行为矩阵测试用例给出的可验证结论仓库在 test/.../ActivityWithAllAnnotationsPermissionsDispatcherTest.kt 中通过 PowerMock 完整验证了生成类的行为这些结论可以直接当作使用手册场景期望行为权限已授予直接调用showCamera()already granted call the method权限未授予且 rationale 为 true不调用目标方法调用showRationaleForCamera(request)权限未授予且 rationale 为 false不调用 rationale 方法onRequestPermissionsResult返回 GRANTED调用showCamera()返回 DENIED 且 rationale 为 true调用showDeniedForCamera()返回 DENIED 且 rationale 为 false调用showNeverAskForCamera()requestCode 与库无关所有回调方法均不触发SDK 23测试中为 22直接依据checkSelfPermission结果决定是否调用目标方法最后两行很重要在 Android 6.0API 23之前的设备上权限在安装时授予无需运行时请求生成代码对此做了兼容分支——测试blow M follows checkSelfPermissions result false/true正是模拟该场景见 ActivityWithAllAnnotationsPermissionsDispatcherTest.kt。七、Java 侧集成配置Gradle要让注解处理器真正跑起来需要在app 模块的build.gradle中添加依赖${latest.version}请以 Maven Central 上的实际版本号为准dependencies { implementation com.github.permissions-dispatcher:permissionsdispatcher:${latest.version} annotationProcessor com.github.permissions-dispatcher:permissionsdispatcher-processor:${latest.version} }Java 项目用annotationProcessorKotlin 项目则改用kapt配合apply plugin: kotlin-kapt详见 README.md 的 Installation 一节。需要注意的是仓库已从 jCenter 迁移至 Maven Central迁移细节见 doc/migration_guide.md。八、进一步实践建议完整可运行示例仓库的sample模块sample/src/main/kotlin/permissions/dispatcher/sample/MainActivity.kt同时演示了单权限CAMERA与多权限READ_CONTACTS WRITE_CONTACTS两种写法还包含用AlertDialog展示 rationale 的showRationaleDialog辅助方法建议对照阅读。maxSdkVersionNeedsPermission还支持maxSdkVersion参数默认 0见 NeedsPermission.java用于声明仅在某个 SDK 版本以下才需要该权限可避免高版本系统上的多余请求详见 doc/maxsdkversion.md。Kotlin 开发者若项目使用 Kotlin官方推荐优先使用ktx模块基于协程与扩展函数见 ktx/README.md或退而使用kapt走与本文完全相同的注解流程。结语使用 PermissionsDispatcher 的 Java 流程可以总结为四步Manifest 声明权限 → 类上加RuntimePermissions、方法上加配套注解 → 在 UI 入口调用生成的xxxWithPermissionCheck(target)→ 在onRequestPermissionsResult中委托给生成的静态方法。整个过程中权限的检查—请求—rationale—拒绝—不再询问状态机完全由注解处理器生成的代码接管你的业务类只需保留最纯粹的有权限时做什么、没权限时提示什么逻辑。【免费下载链接】PermissionsDispatcherA declarative API to handle Android runtime permissions.项目地址: https://gitcode.com/gh_mirrors/pe/PermissionsDispatcher创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考