前言
從 Android 6.0(API 級別 23)開始,用戶開始在應用運行時向其授予權限,而不是在應用安裝時授予。所以如果你的應用使用到了一些危險權限,就必須在AndroidManifest.xml 中靜態地聲明需要用到的權限,並在使用到該功能時要動態的申請,否則在調用到相應權限功能時候,會拋出 SecurityException異常。所以本文探討一下動態權限的申請的正確流程,並把它封裝成一個庫,簡化了申請過程。
權限的分類
在講解之前,先看一下android權限的分類,android權限分爲四類,如下:
1、普通權限
普通權限也叫正常權限,它不需要動態申請,你只需要在用到它的時候在AndroidManifest.xml 中靜態地聲明,然後系統在app運行時就會自動的授予該app相應的權限。這類權限主要在你的app想要接觸app沙盒外的數據或資源的時用到,它不會涉及到系統的操作,也不會泄漏或篡改用戶的隱私數據。如下:
ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
FOREGROUND_SERVICE
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MANAGE_OWN_CALLS
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_COMPANION_RUN_IN_BACKGROUND
REQUEST_COMPANION_USE_DATA_IN_BACKGROUND
REQUEST_DELETE_PACKAGES
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
SET_ALARM
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
2、簽名權限
該類權限只對擁有相同簽名的應用開放。例如某個應用自定義了一個permission 且在權限標籤中加入 android:protectionLevel=”signature”,其他應用想要訪問該應用中的某些數據時,必須要在AndroidManifest.xml中聲明該權限,而且還要與該應用具有相同的簽名,系統會在app運行時自動授予該權限。這類我們用的比較少。
3、危險權限
也叫敏感權限,運行時權限,跟普通權限相反,一旦某個應該獲取了該類權限,用戶的隱私數據就面臨被泄露篡改的風險。所以你想使用該權限就必須在AndroidManifest.xml 中靜態地聲明需要用到的權限,並在使用到該功能時要動態的申請,除非用戶同意該權限,否則你不能使用該權限對應的功能。如下:
可以看到android把危險權限分爲10組,所以申請危險權限的時候都是按組申請,我們只要申請組內的任意一個危險權限就行,當用戶一旦同意授權該危險權限,那麼該權限所對應的權限組中的所有其他權限也會同時被授權。
4、特殊權限
特殊權限我瞭解的有三個,如下:
- SYSTEM_ALERT_WINDOW:設置懸浮窗
- WRITE_SETTINGS:修改系統設置
- REQUEST_INSTALL_PACKAGES: 允許應用安裝未知來源應用
它也是要要申請的,但是它不同於危險權限的申請,危險權限的申請會彈出一個對話框詢問你是否同意,而特殊權限的申請需要跳轉到指定的界面,讓你手動確認同意。
動態權限申請流程
所以動態權限的申請就是申請危險權限或特殊權限,權限的申請在不同的Android版本有不同的行爲,如下:
- 如果設備運行的是 Android 5.1 或更低版本,或者應用的 targetSdkVersion 爲 22 或更低:如果您在 Manifest 中列出了危險權限,則用戶必須在安裝應用時系統會要求用戶授予此權限,如果他們不授予此權限,系統根本不會安裝應用,用戶一旦全部同意授予,他們撤銷權限的唯一方式是卸載應用。
- 如果設備運行的是 Android 6.0 或更高版本,並且應用的 targetSdkVersion爲23 或更高:應用必須在 Manifest 中列出權限,並且它必須在運行時請求其需要的每項危險權限。用戶可以授予或拒絕每項權限,且即使用戶拒絕權限請求,應用仍可以繼續運行有限的功能。用戶可以隨時進入應用的“Settings”中調整應用的動態權限授權。所以你每次使用到該權限的功能時,都要動態申請,因爲用戶有可能在“Settings”界面中把它再次關閉掉。
我這裏討論的是6.0後的動態申請,所以從 Android 6.0開始,無論您的應用面向哪個 API 級別,您都應對應用進行測試,以驗證它在缺少需要的權限時行爲是否正常。
如果還不瞭解動態權限申請的詳細步驟,可以看一下這篇文章:Android 6.0運行權限解析(高級篇)。
這裏我假設大家已經知道那些方法了,我把權限申請的流程分爲單個和多個權限申請,分別畫了個圖。
1、單個權限申請流程
2、多個權限申請流程
3、自定義提示權限組的提示框
上面兩個圖有有提到跳出自定義提示權限組,那麼它主要包含以下內容:
- 1、包含需要授權的權限列表或單個權限提示
- 2、包含跳轉到應用設置授權界面中的跳轉按鈕
- 3、包含放棄授權的取消按鈕,即取消這個提示框
注意:如果用戶不授權,則不能使用該功能或應用無法運行,可以考慮取消第3步的取消按鈕,即無法取消這個提示框,一定要用戶去“Settings”授權。
其他注意點
除了特殊權限外,還有一個location權限也比較特殊,需要通過 **LocationManager的isProviderEnabled(LocationManager.GPS_PROVIDER)**判斷是否打開定位開關後再進行權限申請,如下:
lm = (LocationManager) this.getSystemService(this.LOCATION_SERVICE);
if (lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {//開了定位服務
//請求定位功能
PermissionHelper.getInstance().with(this).requestPermission(
Manifest.permission.ACCESS_FINE_LOCATION,
new IPermissionCallback() {
@Override
public void onAccepted(Permission permission) {
//...
}
@Override
public void onDenied(Permission permission) {
//...
}
}
);
} else {
//跳轉到開啓定位的地方
Toast.makeText(this, "系統檢測到未開啓GPS定位服務,請開啓", Toast.LENGTH_SHORT).show();
Intent intent = new Intent();
intent.setAction(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivityForResult(intent, PRIVATE_CODE);
}
}
結語
本文主要讓讓大家對權限的申請流程有進一步的認識,然後可以通過對動態權限的封裝,將檢測動態權限,請求動態權限,權限設置跳轉,監聽權限設置結果等處理和業務功能隔離開來,業務以後可以非常快速的接入動態權限支持,提高開發效率,更多細節查看PermissionHelper。
參考資料: