安卓6.0許可權申請詳解
安卓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上看相關的文件使用。