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