動(dòng)的 Android 運(yùn)行時(shí)權(quán)限處理完整實(shí)踐)
PermissionsDispatcher 的 Java 使用指南注解驅(qū)動(dòng)的 Android 運(yùn)行時(shí)權(quán)限處理完整實(shí)踐【免費(fèi)下載鏈接】PermissionsDispatcherA declarative API to handle Android runtime permissions.項(xiàng)目地址: https://gitcode.com/gh_mirrors/pe/PermissionsDispatcher導(dǎo)讀本文以 PermissionsDispatcher 官方 Java 文檔doc/java_usage.md為主體完整講解如何在純 Java 項(xiàng)目中用聲明式注解替代繁瑣的checkSelfPermission/requestPermissions樣板代碼。你將掌握 5 個(gè)核心注解的語義、注解處理器自動(dòng)生成的XxxPermissionsDispatcher類的委托方式以及底層生成代碼的真實(shí)結(jié)構(gòu)并最終能在自己的 Activity / Fragment 中落地一套完整、可運(yùn)行、經(jīng)測(cè)試驗(yàn)證的運(yùn)行時(shí)權(quán)限方案。一、先了解 PermissionsDispatcher 的定位PermissionsDispatcher 是一個(gè)基于注解處理annotation processing的運(yùn)行時(shí)權(quán)限庫(kù)你在源碼里用少量注解聲明哪個(gè)方法需要什么權(quán)限、權(quán)限被拒時(shí)做什么編譯期處理器會(huì)自動(dòng)生成一個(gè)輔助類把權(quán)限檢查、請(qǐng)求發(fā)起、結(jié)果回調(diào)、rationale 彈窗等邏輯全部接管。項(xiàng)目 README 明確給出三條特性完全支持 Kotlin / Java、支持特殊權(quán)限Special Permissions、100% 無反射見 README.md。Java 側(cè)走annotationProcessor路徑即本文主題。二、零、準(zhǔn)備 AndroidManifest聲明權(quán)限無論使用哪個(gè)運(yùn)行時(shí)權(quán)限庫(kù)第一步都是先在AndroidManifest.xml中聲明目標(biāo)權(quán)限uses-permission android:nameandroid.permission.CAMERA /運(yùn)行時(shí)權(quán)限dangerous permission必須先在 Manifest 中聲明代碼中才能請(qǐng)求。本文示例以相機(jī)權(quán)限Manifest.permission.CAMERA貫穿始終若使用聯(lián)系人或特殊權(quán)限照葫蘆畫瓢替換權(quán)限名即可。三、一、為類與方法掛上注解PermissionsDispatcher 只引入少量注解API 保持精簡(jiǎn)。下表是官方文檔的注解總覽含各注解的源碼定位AnnotationRequiredDescription源碼位置RuntimePermissions?在類上注冊(cè)一個(gè)Activity或Fragment聲明其需要被權(quán)限框架接管annotation/.../RuntimePermissions.javaNeedsPermission?標(biāo)注真正執(zhí)行需要權(quán)限的操作的方法可指定一個(gè)或多個(gè)權(quán)限annotation/.../NeedsPermission.javaOnShowRationale標(biāo)注解釋為什么需要該權(quán)限的方法接收一個(gè)PermissionRequest對(duì)象用于在用戶輸入后繼續(xù)或中止請(qǐng)求annotation/.../OnShowRationale.javaOnPermissionDenied用戶未授予權(quán)限時(shí)調(diào)用的方法annotation/.../OnPermissionDenied.javaOnNeverAskAgain用戶勾選不再詢問時(shí)調(diào)用的方法annotation/.../OnNeverAskAgain.java注意官方文檔特別強(qiáng)調(diào)——被注解的方法不能是private。這是注解處理器校驗(yàn)規(guī)則的一部分處理器中還有PrivateMethodException等一整套校驗(yàn)異常見 processor/.../exception/PrivateMethodException.kt。下面是一個(gè)完整的最小示例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(); } }關(guān)鍵點(diǎn)拆解NeedsPermission(Manifest.permission.CAMERA)NeedsPermission的value()是String[]類型見 NeedsPermission.java因此可以一次聲明多個(gè)權(quán)限例如NeedsPermission(Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS)——sample 中的showContacts()正是這么寫的見 sample/.../MainActivity.kt。OnShowRationale與PermissionRequestrationale 方法接收一個(gè)PermissionRequest參數(shù)。PermissionRequest只有兩個(gè)方法proceed()與cancel()見 annotation/.../PermissionRequest.java分別表示用戶同意繼續(xù)請(qǐng)求與用戶拒絕。官方文檔補(bǔ)充如果 rationale 方法不指定參數(shù)編譯器會(huì)生成process${NeedsPermissionMethodName}ProcessRequest與cancel${NeedsPermissionMethodName}ProcessRequest兩個(gè)方法可替代PermissionRequest使用例如配合DialogFragment的場(chǎng)景。關(guān)聯(lián)注解的權(quán)限列表必須一致OnShowRationale/OnPermissionDenied/OnNeverAskAgain的value()必須與對(duì)應(yīng)的NeedsPermission完全一致。處理器通過findMatchingMethodForNeeds按權(quán)限值精確配對(duì)見 processor/.../util/Helpers.kt配不上會(huì)直接編譯失敗。四、二、把權(quán)限處理委托給生成類編譯后注解處理器會(huì)為MainActivity生成一個(gè)名為MainActivityPermissionsDispatcher的類命名規(guī)則為[Activity Name] PermissionsDispatcher對(duì)應(yīng)常量GEN_CLASS_SUFFIX PermissionsDispatcher見 processor/.../util/Constants.kt。你唯一要做的就是把這個(gè) helper 類接進(jìn)生命周期回調(diào)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。它內(nèi)部先做權(quán)限狀態(tài)檢查已授權(quán) → 直接調(diào)用你的showCamera()未授權(quán) → 按需走 rationale 或直接發(fā)起系統(tǒng)請(qǐng)求。出口在onRequestPermissionsResult中把回調(diào)轉(zhuǎn)交給MainActivityPermissionsDispatcher.onRequestPermissionsResult(...)由生成代碼根據(jù) requestCode 匹配到對(duì)應(yīng)請(qǐng)求再根據(jù)授權(quán)結(jié)果分發(fā)到showCamera()/showDeniedForCamera()/showNeverAskForCamera()。關(guān)于 requestCode生成類里每個(gè)NeedsPermission方法都有一個(gè)專屬的REQUEST_XXX靜態(tài)常量字段由RequestCodeProvider用AtomicInteger原子遞增產(chǎn)生保證整個(gè)應(yīng)用內(nèi)唯一見 processor/.../RequestCodeProvider.kt。你在onRequestPermissionsResult里無需關(guān)心具體數(shù)值框架會(huì)自己匹配。五、深入原理生成代碼長(zhǎng)什么樣雖然生成代碼在編譯期才產(chǎn)出但從處理器源碼可以精確還原它的結(jié)構(gòu)。JavaBaseProcessorUnit.createTypeSpec顯示生成的類包含見 processor/.../impl/java/JavaBaseProcessorUnit.kt靜態(tài)常量字段每個(gè)注解方法對(duì)應(yīng)REQUEST_XXX請(qǐng)求碼、PERMISSION_XXX權(quán)限字符串?dāng)?shù)組、必要時(shí)還有PENDING_XXX掛起的GrantableRequest以及帶參數(shù)方法所需的參數(shù)緩存字段私有構(gòu)造器類不可實(shí)例化全部以靜態(tài)方法對(duì)外createWithPermissionCheckMethods生成xxxWithPermissionCheck(target)系列入口方法createOnShowRationaleCallbackMethods生成 rationale 回調(diào)鏈createPermissionHandlingMethods生成onRequestPermissionsResult分發(fā)邏輯createPermissionRequestClasses為每個(gè)帶PermissionRequest參數(shù)的方法生成內(nèi)部XxxPermissionRequest類。針對(duì) ActivityJavaActivityProcessorUnit揭示了實(shí)際調(diào)用鏈見 processor/.../impl/java/JavaActivityProcessorUnit.kt權(quán)限狀態(tài)判斷使用androidx.core.app.ActivityCompat/ContextCompat體系rationale 判斷調(diào)用ActivityCompat.shouldShowRequestPermissionRationale(target, permission)發(fā)起請(qǐng)求調(diào)用ActivityCompat.requestPermissions(target, permission, requestCode)。也就是說生成代碼內(nèi)部完全基于 AndroidX 的ActivityCompat實(shí)現(xiàn)這正是 README 所述4.x 僅支持 Jetpack的原因使用 appcompat 的舊項(xiàng)目需要停留在 3.x詳見 README.md 與 doc/migration_guide.md。另外JavaBaseProcessorUnit中還有一張?zhí)厥鈾?quán)限映射表把a(bǔ)ndroid.permission.WRITE_SETTINGS與android.permission.SYSTEM_ALERT_WINDOW分別路由到WriteSettingsHelper/SystemAlertWindowHelper見 JavaBaseProcessorUnit.kt。這兩類權(quán)限不走requestPermissions而是打開系統(tǒng)設(shè)置頁(yè)讓用戶手動(dòng)授權(quán)框架會(huì)生成不同的 helper 處理。詳細(xì)用法見 doc/special_permissions.md。六、行為矩陣測(cè)試用例給出的可驗(yàn)證結(jié)論倉(cāng)庫(kù)在 test/.../ActivityWithAllAnnotationsPermissionsDispatcherTest.kt 中通過 PowerMock 完整驗(yàn)證了生成類的行為這些結(jié)論可以直接當(dāng)作使用手冊(cè)場(chǎng)景期望行為權(quán)限已授予直接調(diào)用showCamera()already granted call the method權(quán)限未授予且 rationale 為 true不調(diào)用目標(biāo)方法調(diào)用showRationaleForCamera(request)權(quán)限未授予且 rationale 為 false不調(diào)用 rationale 方法onRequestPermissionsResult返回 GRANTED調(diào)用showCamera()返回 DENIED 且 rationale 為 true調(diào)用showDeniedForCamera()返回 DENIED 且 rationale 為 false調(diào)用showNeverAskForCamera()requestCode 與庫(kù)無關(guān)所有回調(diào)方法均不觸發(fā)SDK 23測(cè)試中為 22直接依據(jù)checkSelfPermission結(jié)果決定是否調(diào)用目標(biāo)方法最后兩行很重要在 Android 6.0API 23之前的設(shè)備上權(quán)限在安裝時(shí)授予無需運(yùn)行時(shí)請(qǐng)求生成代碼對(duì)此做了兼容分支——測(cè)試blow M follows checkSelfPermissions result false/true正是模擬該場(chǎng)景見 ActivityWithAllAnnotationsPermissionsDispatcherTest.kt。七、Java 側(cè)集成配置Gradle要讓注解處理器真正跑起來需要在app 模塊的build.gradle中添加依賴${latest.version}請(qǐng)以 Maven Central 上的實(shí)際版本號(hào)為準(zhǔn)dependencies { implementation com.github.permissions-dispatcher:permissionsdispatcher:${latest.version} annotationProcessor com.github.permissions-dispatcher:permissionsdispatcher-processor:${latest.version} }Java 項(xiàng)目用annotationProcessorKotlin 項(xiàng)目則改用kapt配合apply plugin: kotlin-kapt詳見 README.md 的 Installation 一節(jié)。需要注意的是倉(cāng)庫(kù)已從 jCenter 遷移至 Maven Central遷移細(xì)節(jié)見 doc/migration_guide.md。八、進(jìn)一步實(shí)踐建議完整可運(yùn)行示例倉(cāng)庫(kù)的sample模塊sample/src/main/kotlin/permissions/dispatcher/sample/MainActivity.kt同時(shí)演示了單權(quán)限CAMERA與多權(quán)限READ_CONTACTS WRITE_CONTACTS兩種寫法還包含用AlertDialog展示 rationale 的showRationaleDialog輔助方法建議對(duì)照閱讀。maxSdkVersionNeedsPermission還支持maxSdkVersion參數(shù)默認(rèn) 0見 NeedsPermission.java用于聲明僅在某個(gè) SDK 版本以下才需要該權(quán)限可避免高版本系統(tǒng)上的多余請(qǐng)求詳見 doc/maxsdkversion.md。Kotlin 開發(fā)者若項(xiàng)目使用 Kotlin官方推薦優(yōu)先使用ktx模塊基于協(xié)程與擴(kuò)展函數(shù)見 ktx/README.md或退而使用kapt走與本文完全相同的注解流程。結(jié)語使用 PermissionsDispatcher 的 Java 流程可以總結(jié)為四步Manifest 聲明權(quán)限 → 類上加RuntimePermissions、方法上加配套注解 → 在 UI 入口調(diào)用生成的xxxWithPermissionCheck(target)→ 在onRequestPermissionsResult中委托給生成的靜態(tài)方法。整個(gè)過程中權(quán)限的檢查—請(qǐng)求—rationale—拒絕—不再詢問狀態(tài)機(jī)完全由注解處理器生成的代碼接管你的業(yè)務(wù)類只需保留最純粹的有權(quán)限時(shí)做什么、沒權(quán)限時(shí)提示什么邏輯?!久赓M(fèi)下載鏈接】PermissionsDispatcherA declarative API to handle Android runtime permissions.項(xiàng)目地址: https://gitcode.com/gh_mirrors/pe/PermissionsDispatcher創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考