您好,登錄后才能下訂單哦!
這篇文章主要介紹“Android動態權限申請功能怎么實現”,在日常操作中,相信很多人在Android動態權限申請功能怎么實現問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Android動態權限申請功能怎么實現”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
為了解決這個痛點,我封裝出了兩個方法,用于隨時隨地快速的動態申請權限,我們先來看看我們的封裝方法是如何調用的:
activity.requestPermission(Manifest.permission.CAMERA, onPermit = { //申請權限成功 Do something }, onDeny = { shouldShowCustomRequest -> //申請權限失敗 Do something if (shouldShowCustomRequest) { //用戶選擇了拒絕并且不在詢問,此時應該使用自定義彈窗提醒用戶授權(可選) } })
這樣是不是非常的簡單便捷?申請和結果回調都在一個方法內處理,并且支持隨用隨調。
那么,這么方便好用的方法是怎么實現的呢?不知道小伙伴們在平時開發中有沒有注意到過,當你調用startActivityForResult
時,AS會提示你該方法已被棄用,點進去看會告訴你應該使用registerForActivityResult
方法替代。沒錯,這就是androidx
給我們提供的ActivityResult
功能,并且這個功能不僅支持ActivityResult
回調,還支持打開文檔,拍攝照片,選擇文件等各種各樣的回調,同樣也包括我們今天要說的權限申請
其實Android在官方文檔 請求運行時權限 中就已經將其作為動態權限申請的推薦方法了,如下示例代碼所示:
val requestPermissionLauncher = registerForActivityResult(RequestPermission() ) { isGranted: Boolean -> if (isGranted) { // Permission is granted. Continue the action or workflow in your // app. } else { // Explain to the user that the feature is unavailable because the // feature requires a permission that the user has denied. At the // same time, respect the user's decision. Don't link to system // settings in an effort to convince the user to change their // decision. } } when { ContextCompat.checkSelfPermission( CONTEXT, Manifest.permission.REQUESTED_PERMISSION ) == PackageManager.PERMISSION_GRANTED -> { // You can use the API that requires the permission. } shouldShowRequestPermissionRationale(...) -> { // In an educational UI, explain to the user why your app requires this // permission for a specific feature to behave as expected, and what // features are disabled if it's declined. In this UI, include a // "cancel" or "no thanks" button that lets the user continue // using your app without granting the permission. showInContextUI(...) } else -> { // You can directly ask for the permission. // The registered ActivityResultCallback gets the result of this request. requestPermissionLauncher.launch( Manifest.permission.REQUESTED_PERMISSION) } }
如果你遵照以上方法這么寫的話,在實際調用的時候會直接發生崩潰:
java.lang.IllegalStateException: LifecycleOwner Activity is attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.
這段報錯很明顯的告訴我們,我們的注冊工作必須要在Activity
聲明周期STARTED
之前進行(也就是onCreate
時和onStart
完成前),但這樣我們就必須要事先注冊好所有可能會用到的權限,沒辦法做到隨時隨地有需要時再申請權限了,有辦法解決這個問題嗎?答案是肯定的。
想解決這個問題,我們必須要知道問題的成因,讓我們帶著問題進到源碼中一探究竟:
public final <I, O> ActivityResultLauncher<I> registerForActivityResult( @NonNull ActivityResultContract<I, O> contract, @NonNull ActivityResultCallback<O> callback) { return registerForActivityResult(contract, mActivityResultRegistry, callback); } public final <I, O> ActivityResultLauncher<I> registerForActivityResult( @NonNull final ActivityResultContract<I, O> contract, @NonNull final ActivityResultRegistry registry, @NonNull final ActivityResultCallback<O> callback) { return registry.register( "activity_rq#" + mNextLocalRequestCode.getAndIncrement(), this, contract, callback); } public final <I, O> ActivityResultLauncher<I> register( @NonNull final String key, @NonNull final LifecycleOwner lifecycleOwner, @NonNull final ActivityResultContract<I, O> contract, @NonNull final ActivityResultCallback<O> callback) { Lifecycle lifecycle = lifecycleOwner.getLifecycle(); if (lifecycle.getCurrentState().isAtLeast(Lifecycle.State.STARTED)) { throw new IllegalStateException("LifecycleOwner " + lifecycleOwner + " is " + "attempting to register while current state is " + lifecycle.getCurrentState() + ". LifecycleOwners must call register before " + "they are STARTED."); } registerKey(key); LifecycleContainer lifecycleContainer = mKeyToLifecycleContainers.get(key); if (lifecycleContainer == null) { lifecycleContainer = new LifecycleContainer(lifecycle); } LifecycleEventObserver observer = new LifecycleEventObserver() { ... }; lifecycleContainer.addObserver(observer); mKeyToLifecycleContainers.put(key, lifecycleContainer); return new ActivityResultLauncher<I>() { ... }; }
我們可以發現,registerForActivityResult
實際上就是調用了ComponentActivity
內部成員變量的mActivityResultRegistry.register
方法,而在這個方法的一開頭就檢查了當前Activity
的生命周期,如果生命周期位于STARTED
后則直接拋出異常,那我們該如何繞過這個限制呢?
其實在register
方法的下面就有一個同名重載方法,這個方法并沒有做生命周期的檢測:
public final <I, O> ActivityResultLauncher<I> register( @NonNull final String key, @NonNull final ActivityResultContract<I, O> contract, @NonNull final ActivityResultCallback<O> callback) { registerKey(key); mKeyToCallback.put(key, new CallbackAndContract<>(callback, contract)); if (mParsedPendingResults.containsKey(key)) { @SuppressWarnings("unchecked") final O parsedPendingResult = (O) mParsedPendingResults.get(key); mParsedPendingResults.remove(key); callback.onActivityResult(parsedPendingResult); } final ActivityResult pendingResult = mPendingResults.getParcelable(key); if (pendingResult != null) { mPendingResults.remove(key); callback.onActivityResult(contract.parseResult( pendingResult.getResultCode(), pendingResult.getData())); } return new ActivityResultLauncher<I>() { ... }; }
找到這個方法就簡單了,我們將registerForActivityResult
方法調用替換成activityResultRegistry.register
調用就可以了
當然,我們還需要注意一些小細節,檢查生命周期的register
方法同時也會注冊生命周期回調,當Activity
被銷毀時會將我們注冊的ActivityResult
回調移除,我們也需要給我們封裝的方法加上這個邏輯,最終實現就如下所示。
private val nextLocalRequestCode = AtomicInteger() private val nextKey: String get() = "activity_rq#${nextLocalRequestCode.getAndIncrement()}" fun ComponentActivity.requestPermission( permission: String, onPermit: () -> Unit, onDeny: (shouldShowCustomRequest: Boolean) -> Unit ) { if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) { onPermit() return } var launcher by Delegates.notNull<ActivityResultLauncher<String>>() launcher = activityResultRegistry.register( nextKey, ActivityResultContracts.RequestPermission() ) { result -> if (result) { onPermit() } else { onDeny(!ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) } launcher.unregister() } lifecycle.addObserver(object : LifecycleEventObserver { override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { if (event == Lifecycle.Event.ON_DESTROY) { launcher.unregister() lifecycle.removeObserver(this) } } }) launcher.launch(permission) } fun ComponentActivity.requestPermissions( permissions: Array<String>, onPermit: () -> Unit, onDeny: (shouldShowCustomRequest: Boolean) -> Unit ) { var hasPermissions = true for (permission in permissions) { if (ContextCompat.checkSelfPermission( this, permission ) != PackageManager.PERMISSION_GRANTED ) { hasPermissions = false break } } if (hasPermissions) { onPermit() return } var launcher by Delegates.notNull<ActivityResultLauncher<Array<String>>>() launcher = activityResultRegistry.register( nextKey, ActivityResultContracts.RequestMultiplePermissions() ) { result -> var allAllow = true for (allow in result.values) { if (!allow) { allAllow = false break } } if (allAllow) { onPermit() } else { var shouldShowCustomRequest = false for (permission in permissions) { if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) { shouldShowCustomRequest = true break } } onDeny(shouldShowCustomRequest) } launcher.unregister() } lifecycle.addObserver(object : LifecycleEventObserver { override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { if (event == Lifecycle.Event.ON_DESTROY) { launcher.unregister() lifecycle.removeObserver(this) } } }) launcher.launch(permissions) }
到此,關于“Android動態權限申請功能怎么實現”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。