欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android動(dòng)態(tài)權(quán)限申請?jiān)斀?/h1>
 更新時(shí)間:2023年04月25日 10:41:23   作者:dreamgyf  
大家是否還在為動(dòng)態(tài)權(quán)限申請感到苦惱呢?傳統(tǒng)的動(dòng)態(tài)權(quán)限申請需要在Activity中重寫onRequestPermissionsResult方法來接收用戶權(quán)限授予的結(jié)果,本文介紹了如何簡單的實(shí)現(xiàn)Android動(dòng)態(tài)權(quán)限申請

前言

注:只想看實(shí)現(xiàn)的朋友們可以直接跳到最后面的最終實(shí)現(xiàn)

大家是否還在為動(dòng)態(tài)權(quán)限申請感到苦惱呢?傳統(tǒng)的動(dòng)態(tài)權(quán)限申請需要在Activity中重寫onRequestPermissionsResult方法來接收用戶權(quán)限授予的結(jié)果。試想一下,你需要在一個(gè)子模塊中申請權(quán)限,那得從這個(gè)模塊所在的ActivityonRequestPermissionsResult中將結(jié)果一層層再傳回到這個(gè)模塊中,相當(dāng)?shù)穆闊?,代碼也相當(dāng)冗余和不干凈,逼死強(qiáng)迫癥。

使用

為了解決這個(gè)痛點(diǎn),我封裝出了兩個(gè)方法,用于隨時(shí)隨地快速的動(dòng)態(tài)申請權(quán)限,我們先來看看我們的封裝方法是如何調(diào)用的:

activity.requestPermission(Manifest.permission.CAMERA, onPermit = {
    //申請權(quán)限成功 Do something
}, onDeny = { shouldShowCustomRequest ->
    //申請權(quán)限失敗 Do something
    if (shouldShowCustomRequest) {
        //用戶選擇了拒絕并且不在詢問,此時(shí)應(yīng)該使用自定義彈窗提醒用戶授權(quán)(可選)
    }
})

這樣是不是非常的簡單便捷?申請和結(jié)果回調(diào)都在一個(gè)方法內(nèi)處理,并且支持隨用隨調(diào)。

方案

那么,這么方便好用的方法是怎么實(shí)現(xiàn)的呢?不知道小伙伴們在平時(shí)開發(fā)中有沒有注意到過,當(dāng)你調(diào)用startActivityForResult時(shí),AS會(huì)提示你該方法已被棄用,點(diǎn)進(jìn)去看會(huì)告訴你應(yīng)該使用registerForActivityResult方法替代。沒錯(cuò),這就是androidx給我們提供的ActivityResult功能,并且這個(gè)功能不僅支持ActivityResult回調(diào),還支持打開文檔,拍攝照片,選擇文件等各種各樣的回調(diào),同樣也包括我們今天要說的權(quán)限申請

其實(shí)Android在官方文檔 請求運(yùn)行時(shí)權(quán)限 中就已經(jīng)將其作為動(dòng)態(tài)權(quán)限申請的推薦方法了,如下示例代碼所示:

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)
    }
}

說到這里,可能有小伙伴要質(zhì)疑我了:“官方文檔里都寫明了的東西,你還特地寫一遍,還起了這么個(gè)標(biāo)題,是不是在水文章?!”

莫急,如果你遵照以上方法這么寫的話,在實(shí)際調(diào)用的時(shí)候會(huì)直接發(fā)生崩潰:

java.lang.IllegalStateException: 
LifecycleOwner Activity is attempting to register while current state is RESUMED. 
LifecycleOwners must call register before they are STARTED.

這段報(bào)錯(cuò)很明顯的告訴我們,我們的注冊工作必須要在Activity聲明周期STARTED之前進(jìn)行(也就是onCreate時(shí)和onStart完成前),但這樣我們就必須要事先注冊好所有可能會(huì)用到的權(quán)限,沒辦法做到隨時(shí)隨地有需要時(shí)再申請權(quán)限了,有辦法解決這個(gè)問題嗎?答案是肯定的。

繞過生命周期檢測

想解決這個(gè)問題,我們必須要知道問題的成因,讓我們帶著問題進(jìn)到源碼中一探究竟:

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>() { ... };
}

我們可以發(fā)現(xiàn),registerForActivityResult實(shí)際上就是調(diào)用了ComponentActivity內(nèi)部成員變量的mActivityResultRegistry.register方法,而在這個(gè)方法的一開頭就檢查了當(dāng)前Activity的生命周期,如果生命周期位于STARTED后則直接拋出異常,那我們該如何繞過這個(gè)限制呢?

其實(shí)在register方法的下面就有一個(gè)同名重載方法,這個(gè)方法并沒有做生命周期的檢測:

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>() { ... };
}

找到這個(gè)方法就簡單了,我們將registerForActivityResult方法調(diào)用替換成activityResultRegistry.register調(diào)用就可以了

當(dāng)然,我們還需要注意一些小細(xì)節(jié),檢查生命周期的register方法同時(shí)也會(huì)注冊生命周期回調(diào),當(dāng)Activity被銷毀時(shí)會(huì)將我們注冊的ActivityResult回調(diào)移除,我們也需要給我們封裝的方法加上這個(gè)邏輯,最終實(shí)現(xiàn)就如下所示。

最終實(shí)現(xiàn)

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)
}

總結(jié)

其實(shí)很多實(shí)用技巧本質(zhì)上都是很簡單的,但沒有接觸過就很難想到,我將我的開發(fā)經(jīng)驗(yàn)分享給大家,希望能幫助到大家。

到此這篇關(guān)于Android動(dòng)態(tài)權(quán)限申請?jiān)斀獾奈恼戮徒榻B到這了,更多相關(guān)Android動(dòng)態(tài)權(quán)限申請內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論