項(xiàng)目地址
github:PermissionsDemo
相關(guān)系統(tǒng)方法說(shuō)明
ActivityCompat.checkSelfPermission() :檢查用戶是否已經(jīng)授權(quán); ActivityCompat.requestPermissions() :申請(qǐng)權(quán)限; onRequestPermissionsResult():Activity/Fragment中的回調(diào),用于判斷申請(qǐng)結(jié)果; shouldShowRequestPermissionRationale() :判斷是否能彈出“允許、禁止且不再詢問(wèn)”提示框。
首先需要了解動(dòng)態(tài)權(quán)限申請(qǐng)總共有哪些現(xiàn)象
vivo IQOO3 請(qǐng)求權(quán)限為例
調(diào)用申請(qǐng)權(quán)限方法:ActivityCompat.requestPermissions()
第一次申請(qǐng)彈出:“允許、禁止”,點(diǎn)擊“禁止”;

點(diǎn)擊“禁止且不再詢問(wèn)”,調(diào)用方法則不再?gòu)棿埃?/p>
再次調(diào)用申請(qǐng)方法,不再?gòu)棿?。Activity/Fragment的onRequestPermissionsResult方法中回調(diào):grantResults[i] == PackageManager.PERMISSION_DENIED
我們需要判斷以上三種場(chǎng)景狀態(tài)
相關(guān)方法:ActivityCompat.shouldShowRequestPermissionRationale
此方法僅能在申請(qǐng)權(quán)限,用戶選擇“禁止”之后,判斷是否能彈出“允許、禁止且不再詢問(wèn)”提示框; 在1、第一次申請(qǐng)權(quán)限;2、點(diǎn)擊“禁止且不再詢問(wèn)”。這兩種情形都是返回false。 此方法唯一作用是,判斷返回true時(shí),是可以彈出“允許、禁止且不再詢問(wèn)”提示框。返回false則多種情形都會(huì)出現(xiàn),無(wú)區(qū)別判斷意義。
其他方法
AppOpsManager相關(guān)權(quán)限API只能判斷是否有權(quán)限 MODE無(wú)效 反射也是返回值只有0、1,因此此API在此處無(wú)意義;
總結(jié)
并無(wú)完美方法判斷三種權(quán)限申請(qǐng)場(chǎng)景。
根據(jù)能判斷的情況分為3種場(chǎng)景類(lèi)型:
1、允許權(quán)限 2、禁止 禁止,但沒(méi)有選擇“以后不再詢問(wèn)”,以后申請(qǐng)權(quán)限,會(huì)繼續(xù)彈出提示 3、其他 場(chǎng)景一:選擇“禁止并不再詢問(wèn)”; 場(chǎng)景二:用戶點(diǎn)擊系統(tǒng)申請(qǐng)權(quán)限彈出框外部,使對(duì)話框消失; 場(chǎng)景三:再此之前已經(jīng)點(diǎn)擊過(guò)"禁止并不再詢問(wèn)",調(diào)用申請(qǐng)權(quán)限則直接回調(diào)到此處。
我們來(lái)看看處理方案
既無(wú)完美,始終要有方案處理。
1、網(wǎng)上常用方案
申請(qǐng)權(quán)限,在Activity/Fragment的onRequestPermissionsResult方法中回調(diào),判斷grantResults[i] == PackageManager.PERMISSION_GRANTED,則彈出跳往設(shè)置的提示框
在此會(huì)有兩種情形: 1、申請(qǐng)權(quán)限對(duì)話框彈出時(shí),用戶點(diǎn)擊“禁止”,彈出跳往設(shè)置提示框; 2、用戶已經(jīng)禁止詢問(wèn)時(shí),調(diào)用系統(tǒng)申請(qǐng)權(quán)限方法,直接彈出跳往設(shè)置提示框;
2、簡(jiǎn)易方案
相關(guān)工具方法,Demo示例,github:PermissionsDemo
申請(qǐng)權(quán)限,在Activity/Fragment的onRequestPermissionsResult方法中回調(diào),通過(guò)定義requestCode來(lái)判斷哪次申請(qǐng),再判斷。
可以自行根據(jù)細(xì)分場(chǎng)景處理。
if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
//處理允許權(quán)限后的操作
retrun;
}
String permission = permissions[i];
boolean shouldShow = ActivityCompat.shouldShowRequestPermissionRationale(activity, permission);
if (shouldShow) {
//禁止,但沒(méi)有選擇“以后不再詢問(wèn)”,以后申請(qǐng)權(quán)限,會(huì)繼續(xù)彈出提示
//處理用戶點(diǎn)擊禁止后的操作
} else {
//場(chǎng)景一:選擇“禁止并不再詢問(wèn)”;場(chǎng)景二:用戶點(diǎn)擊系統(tǒng)申請(qǐng)權(quán)限彈出框外部,使對(duì)話框消失;場(chǎng)景三:再此之前已經(jīng)點(diǎn)擊過(guò)"禁止并不再詢問(wèn)",調(diào)用申請(qǐng)權(quán)限則直接回調(diào)到此處。
//Toast提示用戶前往設(shè)置允許權(quán)限
}
3、使用比較流行的框架 RxPermission
優(yōu)點(diǎn):可以直接拿到回調(diào) 缺點(diǎn):需要在FragmentActivity 、Fragment中使用 注意:使用時(shí)傳入的FragmentActivity 或Fragment中,onRequestPermissionsResult方法的父類(lèi)方法不能刪除,否則影響rxPermission的回調(diào)
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
//父類(lèi)方法不能刪除,否則影響rxPermission的回調(diào)
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
使用步驟:
1)引用依賴
project中g(shù)radle添加
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
app中g(shù)radle添加
dependencies {
/* 需要多加對(duì)應(yīng)版本的rxjava,0.12對(duì)應(yīng)rxjava3 */
// RxJava
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
implementation 'io.reactivex.rxjava3:rxjava:3.0.0'
//rxpermissions
implementation 'com.github.tbruyelle:rxpermissions:0.12'
//如果想要嘗試使用 RxView 時(shí)
implementation 'com.jakewharton.rxbinding4:rxbinding:4.0.0'
}
如果項(xiàng)目中使用的是rxjava2,則使用
// RxJava
implementation 'io.reactivex.rxjava2:rxjava:2.0.1'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
//如果想要嘗試使用 RxView 時(shí)
implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
//rxpermissions
implementation 'com.github.tbruyelle:rxpermissions:0.10.2'
2)gradle報(bào)錯(cuò)處理
Android Studio錯(cuò)誤提示Duplicate class android.support.v4.app.INotificationSideChannel found
需要在gradle.properties中添加下面兩行代碼 這是因?yàn)榛旌现С謳?kù)。通過(guò)添加這些行選擇androidX作為支持庫(kù)
android.useAndroidX=true
android.enableJetifier=true
3)以下我們梳理下使用的場(chǎng)景,完整封裝請(qǐng)?jiān)赿emo(PermissionsDemo)中查看:
A、簡(jiǎn)單回調(diào)處理,只回調(diào)允許或者拒絕
permissions.request(permissionstr)方法參數(shù)為可變參數(shù),如果是多個(gè)權(quán)限,則是所有權(quán)限都通過(guò)才回調(diào)true
String[] permissionstr = {
Manifest.permission.RECORD_AUDIO};
RxPermissions permissions = new RxPermissions(activity);
permissions.setLogging(true);
permissions.request(permissionstr).subscribe(new Consumer<Boolean>() {
@Override
public void accept(Boolean aBoolean) {
}
});
B、區(qū)分三種場(chǎng)景回調(diào)處理
RxPermissions.requestEach 方法參數(shù)為可變參數(shù),可傳單個(gè)或者多個(gè)String,或者String[]。 多個(gè)權(quán)限則通過(guò)方法 permission.name.equalsIgnoreCase() 來(lái)區(qū)分
String[] permissionstr = {
Manifest.permission.RECORD_AUDIO}
RxPermissions permissions = new RxPermissions(activity);
permissions.setLogging(true);
permissions.requestEach(permissionStr)
.subscribe(new Consumer<Permission>() {
@Override
public void accept(Permission permission) throws Exception {
if (permission.name.equalsIgnoreCase(Manifest.permission.RECORD_AUDIO)) {
if (permission.granted) {
//處理允許權(quán)限后的操作
return;
}
if (permission.shouldShowRequestPermissionRationale) {
//禁止,但沒(méi)有選擇“以后不再詢問(wèn)”,以后申請(qǐng)權(quán)限,會(huì)繼續(xù)彈出提示
//處理用戶點(diǎn)擊禁止后的操作
return;
}
//場(chǎng)景一:選擇“禁止并不再詢問(wèn)”;
//場(chǎng)景二:用戶點(diǎn)擊系統(tǒng)申請(qǐng)權(quán)限彈出框外部,使對(duì)話框消失;
//場(chǎng)景三:再此之前已經(jīng)點(diǎn)擊過(guò)"禁止并不再詢問(wèn)",調(diào)用申請(qǐng)權(quán)限則直接回調(diào)到此處。
//Toast提示用戶前往設(shè)置允許權(quán)限
}
}
});
C、區(qū)分三種場(chǎng)景回調(diào),多個(gè)權(quán)限申請(qǐng),合并結(jié)果處理。
RxPermissions permissions = new RxPermissions(activity);
permissions.setLogging(true);
permissions.requestEachCombined(permissionStr)
.subscribe(new Consumer<Permission>() {
@Override
public void accept(Permission permission) throws Exception {
if (permission.granted) {
//處理允許權(quán)限后的操作
return;
}
if (permission.shouldShowRequestPermissionRationale) {
//禁止,但沒(méi)有選擇“以后不再詢問(wèn)”,以后申請(qǐng)權(quán)限,會(huì)繼續(xù)彈出提示
//處理用戶點(diǎn)擊禁止后的操作
return;
}
//場(chǎng)景一:選擇“禁止并不再詢問(wèn)”;
//場(chǎng)景二:用戶點(diǎn)擊系統(tǒng)申請(qǐng)權(quán)限彈出框外部,使對(duì)話框消失;
//場(chǎng)景三:再此之前已經(jīng)點(diǎn)擊過(guò)"禁止并不再詢問(wèn)",調(diào)用申請(qǐng)權(quán)限則直接回調(diào)到此處。
//Toast提示用戶前往設(shè)置允許權(quán)限
}
});
其他問(wèn)題
獲取權(quán)限名稱
網(wǎng)上方法PermissionInfo .loadLabel(pm).toString(),無(wú)法獲得權(quán)限名稱。 需自己做個(gè)定義來(lái)獲取權(quán)限名稱,在PermissionsDemo中已經(jīng)寫(xiě)好工具類(lèi)com.hero.simplepermissionsdemo.PermissionNameEnum,直接使用即可。
給小白的提示
系統(tǒng)權(quán)限分為兩類(lèi):正常權(quán)限和危險(xiǎn)權(quán)限。
正常權(quán)限:不會(huì)直接給用戶隱私權(quán)帶來(lái)風(fēng)險(xiǎn)。如果您的應(yīng)用在其清單中列出了正常權(quán)限,系統(tǒng)將自動(dòng)授予該權(quán)限。
危險(xiǎn)權(quán)限:會(huì)授予應(yīng)用訪問(wèn)用戶機(jī)密數(shù)據(jù)的權(quán)限。如果您的應(yīng)用在其清單中列出了正常權(quán)限,系統(tǒng)將自動(dòng)授予該權(quán)限。如果您列出了危險(xiǎn)權(quán)限,則用戶必須明確批準(zhǔn)您的應(yīng)用使用這些權(quán)限。
記得在在AndroidManifest.xml中添加所需權(quán)限
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.hero.simplepermissionsdemo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.SimplePermissionsDemo">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
</manifest>