安卓6.0的一大變化就是對于權限的限制,首次安裝應用時會産生一個權限請求清單,需要使用者手動逐個确認每個權限,應用才能擷取該權限。而在6.0之前預設開啟的,是以會産生一些應用會讀取使用者的一些隐私資訊,影響使用者體驗。本文根據實際項目開發經驗,簡述基于安卓6.0開發的動态權限調用相關的API使用,友善開發者快速調用。
一、安卓6.0權限相關介紹
Google将權限分為兩類,一類是Normal Permissions,該類權限不涉及使用者隐私,不需要使用者進行授權,比如藍牙、通路網絡等;另一類是Dangerous Permission,一般是涉及使用者隐私,需要使用者手動授權,比如錄影機、擷取手機狀态和讀寫SD卡等。
- Normal Permissions:
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
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
- Dangerous Permissions:
group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS
group:android.permission-group.PHONE
permission:android.permission.READ_CALL_LOG
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.USE_SIP
permission:android.permission.PROCESS_OUTGOING_CALLS
permission:com.android.voicemail.permission.ADD_VOICEMAIL
group:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDAR
permission:android.permission.WRITE_CALENDAR
group:android.permission-group.CAMERA
permission:android.permission.CAMERA
group:android.permission-group.SENSORS
permission:android.permission.BODY_SENSORS
group:android.permission-group.LOCATION
permission:android.permission.ACCESS_FINE_LOCATION
permission:android.permission.ACCESS_COARSE_LOCATION
group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE
group:android.permission-group.MICROPHONE
permission:android.permission.RECORD_AUDIO
group:android.permission-group.SMS
permission:android.permission.READ_SMS
permission:android.permission.RECEIVE_WAP_PUSH
permission:android.permission.RECEIVE_MMS
permission:android.permission.RECEIVE_SMS
permission:android.permission.SEND_SMS
permission:android.permission.READ_CELL_BROADCASTS
可以通過指令行檢視手機的Dangerous Permissions權限清單。
adb shell pm list permissions -d -g
細心的讀者會發現Dangerous Permissions的權限都是一組一組的,确實,Google就是這樣設計的,但是對于分組機制也會給我的應用帶你一定的風險,因為6.0及以上機器對于危險授權的機制是這樣的:假如應用已被使用者授權了同一組的某個危險權限,那麼系統會立即授權同組的其他權限,而不需使用者手動授權。例如你的應用對WRITE_CONTACTS授權了,那麼對于CONTACTS該組内的其他權限也是授權的。在彈出權限授權對話框的授權請求,也是指對該組權限的授權申請。
二、安卓6.0動态申請權限實作
安卓6.0(23)動态權限的申請包括:AndroidManifest添加、檢查權限、申請授權和權限處理回調四個過程。
-
AndroidManifest添加
在AndroidManifest添加相關請求的權限,該操作與之前的權限添加類似,這裡要聲明一下:即使動态申請權限,也需要在此處添加相關的權限,否則無法動态申請。因為代碼邏輯處理的權限申請的檢查以及申請回調。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.XXX"/> //XXX:需要申請的權限名稱
-
檢查權限
檢查權限的邏輯處理一般是在應用啟動的Activity的onCreate()中實作。
ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED
ContextCompat.checkSelfPermissionf()方法是檢查某個權限是否已經被授權,第一個參數為我們目前的Activity的引用,第二個參數為我們申請的權限,該方法的傳回值為:
PackageManager.PERMISSION_DENIED:拒絕
PackageManager.PERMISSION_GRANTED:授權
一般我們都是判斷其傳回值是否為GRANTED,如不是則判斷未授權,申請授權;否則為已經授權。
- 申請授權
MainActivity.this.requestPermissions(permissions, requestCode);
其中permissions為申請權限的字元串數組;requestCode用于回調的監測。在申請授權時我們可以一次請求申請多個權限。
一般使用時,權限的請求和申請授權的操作是一起處理的。監測我們需要申請的權限是否已經授權,如果授權,則調用授權後的邏輯處理;否則,調用權限申請的邏輯。如果之前已經開啟并授權成功,那麼不會顯示權限請求的對話框,邏輯進入“已經授權”的進行中。簡單的邏輯處理如下。
String[] permissions = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};
if (Build.VERSION.SDK_INT >= 23) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
//申請攝像頭權限
if (Manifest.permission.CAMERA.equals(Manifest.permission.CAMERA)) {
Log.i(TAG, "requestPermission: 請求攝像頭權限");
this.requestPermissions(permissions, CAMERA_CODE);
}
} else {
Log.i(TAG, "requestPermission:已經申請了攝像頭");
}
//申請讀寫權限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
if (Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
Log.i(TAG, "requestPermission: 請求外部讀寫權限");
this.requestPermissions(permissions, WRITE_EXTERNAL_STORAGE_CODE);
}
} else {
Log.i(TAG, "requestPermission: 已經申請了外部讀寫權限");
}
}
- 權限處理回調
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Log.i(TAG, "onRequestPermissionsResult: " + grantResults.length);
switch (requestCode) {
case CAMERA_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "onRequestPermissionsResult: 攝像頭權限請求成功");
} else {
Log.i(TAG, "onRequestPermissionsResult: 攝像頭權限請求失敗");
}
break;
case WRITE_EXTERNAL_STORAGE_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.i(TAG, "onRequestPermissionsResult: 讀寫權限請求成功");
} else {
Log.i(TAG, "onRequestPermissionsResult: 讀寫權限請求失敗");
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
權限請求的回調,隻需重寫onRequestPermissionsResult()方法,根據我們在權限請求的requestCode作為區分不同權限請求的回調判斷。例如本例中針對CAMERA_CODE和WRITE_EXTERNAL_STORAGE_CODE分别處理。grantResults對于申請的請求結果,在請求數組包含幾個請求字元串,這裡的回調數字就是多少。通過判斷grantResults[i]對應的結果是否為PackageManager.PERMISSION_GRANTED來判決權限請求是否成功,如果我們在應用請求時,選擇拒絕,則此處回調顯示請求失敗。
由于此處包含授權成功的回調,是以我們在應用開發時,要注意在應用回調成功和失敗時的分别邏輯處理。
至此,安卓6.0動态請求權限的邏輯處理講解結束,總結一下:
**1、AndroidManifest添加:**該添加過程與此前的權限添加一樣,根據我們需要添加相應權限(注意:動态申請的也要添加);
**2、檢查權限和申請授權:**監測SDK是否大于23,如大于則動态申請權限;包括兩個重要方法監測權限(ContextCompat.checkSelfPermission())和請求權限(MainActivity.this.requestPermissions())。如果監測已經請求了權限,則直接處理相應邏輯;
**3、權限處理回調:**根據requestCode判斷相應的權限請求回調,根據grantResults[i]判斷權限請求結果,然後做相應的邏輯處理。
除了我們自己編寫外,我們還可以借助網上一些開源架構,例如PermissionGen,我們可直接調用即可,詳情可以在GitHub上看相關的文檔使用。