一、基本介紹
1、正常權限(安裝時權限):
不會直接給用戶隱私權帶來風險。如果您的應用在其清單中列出了正常權限,系統將自動授予該權限。
例如,設置時區的權限就是正常權限。如果應用聲明其需要正常權限,系統會自動向應用授予該權限。
2、危險權限(運行時權限):
會授予應用訪問用戶機密數據的權限。如果您列出了危險權限,則用戶必須明確批准您的應用使用這些權限。
3、注意:
- 在 Android 5.1(API 22)或更低版本,並且應用的 targetSdkVersion 是 22 或更低版本,則系統會在安裝時要求用戶授予權限。(沿用之前的權限系統),也就是在程序安裝的時候程序會將所有需要的權限全部列出來,其設計思路非常簡單,就是用戶如果認可你所申請的權限,那么就會安裝你的程序,如果不認可你所申請的權限,則會拒絕安裝。
- 從Android6.0版本開始才開始加入運行時權限,也就是說用戶不需要在安裝軟件的時候一次性授權所有的權限,而是可以在軟件的使用過程中再對某一項權限進行申請使用,這就是運行時權限。
- 在請求權限時,系統只告訴用戶應用需要的權限組,而不告知具體權限。(如上圖)
二、運行時權限(使用原生API)
1、步驟
-
首先要在清單文件中申請所需要的權限。

-
將硬件聲明為可選(uses-feature)以及要確定硬件可用性


3. 按API級別申明權限
當目標sdk大於等於29的時候配置android.permission.WRITE_EXTERNAL_STORAGE會出現下面的警報:
WRITE_EXTERNAL_STORAGE no longer provides write access when targeting Android 10+
這時候需要設置根據Api級別申明權限android:maxSdkVersion,該屬性表示設備搭載的系統版本高於 maxSdkVersion 時不需要特定權限。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"
tools:ignore="ScopedStorage"/>
-
請求應用權限,其邏輯應該如下圖所示

-
首先檢查是否已經授予了某個權限,使用
ContextCompat.checkSelfPermission()此方法會返回PERMISSION_GRANTED或PERMISSION_DENIED。如果ContextCompat.checkSelfPermission()方法返回PERMISSION_DENIED,請調用shouldShowRequestPermissionRationale()。如果此方法返回true,請向用戶顯示指導界面,在此界面中說明用戶希望啟用的功能為何需要特定權限。(返回true說明應用之前請求過此權限但用戶拒絕了請求,此方法將返回 true),之后再調用requestPermissions函數進行權限的請求,這個函數會引起onRequestPermissionsResult的回調。private void callPhone(){ if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { // 應用沒有授予撥打電話權限,請求權限 requestPhoneCallPermission(); } else { // 應用被授予撥打電話權限 PackageManager.PERMISSION_GRANTED makeCall(); } }private void requestPhoneCallPermission() { // 如果應用之前請求過此權限但用戶拒絕了請求,此方法將返回 true if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)){ // 向用戶詳細解釋申請該權限的原因 new AlertDialog.Builder(this) .setCancelable(false) .setMessage("撥打電話需要使用電話權限,如果不授予權限會導致該功能無法正常使用") .setPositiveButton("好的", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ActivityCompat.requestPermissions( MainActivity.this, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CALLPHONE//注意這個REQUEST_CALLPHONE會傳給回調函數的requestCode ); } }) .setNegativeButton("拒絕", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }) .show(); } else { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CALLPHONE); } }下面是回調函數:
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == REQUEST_CALLPHONE) { if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 授予權限,撥打電話 makeCall(); } else { Toast.makeText(this, "請求權限被拒絕", Toast.LENGTH_SHORT).show(); } } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } }
2、結果展示
當需要打電話的時候第一次請求:
三、運行時權限(使用PermissionsDispatcher)
該工具直接使用注解來自動幫我們生成權限請求代碼,使用時需要在項目中添加如下兩個注解:
implementation "com.github.permissions-dispatcher:permissionsdispatcher:4.8.0"
annotationProcessor "com.github.permissions-dispatcher:permissionsdispatcher-processor:4.8.0"
| 注解 | 是否必須 | 作用 |
|---|---|---|
| @RuntimePermissions | 是 | 這是必須使用的注解,標記Activity/Fragment,則注解解釋器會生成對應類的代碼 |
| @NeedsPermission | 是 | 標記需要請求使用權限的方法,也就是說你獲取了相應的權限之后就會執行這個方法,可以在括號里面加一個權限或者多個權限。 |
| @OnShowRationale | 否 | 對應之前的shouldShowRequestPermissionRationale(),當應用之前請求過此權限但用戶拒絕了請求,再次請求時調用,告知用戶為什么必須要授權這個權限,注解括號里面有參數,傳入想要申請的權限,而且這個方法還要傳入一個PermissionRequest對象,這個對象有兩種方法:proceed()讓權限請求繼續,cancel()讓請求中斷。也就是說,這個方法會攔截你發出的請求,這個方法用於告訴用戶你接下來申請的權限是干嘛的,說服用戶給你權限。 |
| @OnPermissionDenied | 否 | 當請求權限遭拒絕時調用,可以告知用戶已經拒絕該權限 |
| @OnNeverAskAgain | 否 | 當用戶勾選不再提示,並拒絕權限時,再次請求時調用(如上圖中禁止之后不再詢問按鈕)也就是說,我們可以在這個方法做申請權限失敗並選擇不再詢問之后的處理。例如,可以告訴作者想開啟權限的就從手機設置里面開啟。 |
注意:上面這些注解的方法都不能是private,原因看下面
使用PermissionsDispatcher除了要實現注解之外,還要重寫Activity的onRequestPermissionsResult()方法,在里面讓一個PermissionsDispatcher執行回調。這個PermissionsDispatcher是什么來的呢?
原來只要我們實現了@RuntimePermissions和@NeedsPermission這兩個必須的注解之后,再build一次project之后,編譯器就會在在app\build\intermediates\classes\debug目錄下與被注解的Activity同一個包下生成一個輔助類,名稱為 “被注解的Activity的名稱+PermissionsDispatcher” 的輔助類,用來調用被注解的Activity的方法(就是因為這個所以被注解的方法不能private,private方法的作用域不在其他的類)。所以,第一次用的話,要注解好之后,build一次,下面的方法里面的PermissionsDispatcherActivityPermissionsDispatcher才不會令AS報紅。
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
PermissionsDispatcherActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
}
注意代碼中的onclick方法PermissionsDispatcherActivityPermissionsDispatcher的takePhotoWithPermissionCheck方法在第一次編譯完成之后會自動生成。
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
PermissionsDispatcherActivityPermissionsDispatcher.takePhotoWithPermissionCheck(PermissionsDispatcherActivity.this);
}
});
上面的具體代碼鏈接:
https://github.com/Haoocker/Android_Learn/tree/master/RuntimePermissionDemo
