天天看點

安卓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上看相關的文檔使用。