關於動態許可權

龍湫發表於2018-05-15

首發於我的公眾號

關於動態許可權

1、在給app升級動態許可權之前需要注意搞清楚一下幾個概念

1.1、版本號對應的api等級

查閱官網可以看出

關於動態許可權

1.2、minSdkVersion、targetSdkVersion和 compileSdkVersion

這幾個屬性通常在gradle defaultConfig中去配置,作用如下

(1)minSdkVersion 指明應用程式執行所需的最小API level,如果系統的API level低於minSdkVersion設定的值,那麼android系統會阻止使用者安裝這個應用,如果不指明的話,預設是1,如果指明這個屬性,並在專案中使用了改與這個API level的API的話,那麼將會在編譯時報錯。 可見該屬性不僅在程式安裝時起作用,而且會在專案構建時起作用

(2)targetSdkVersion

指明應用程式目標API level的一個整數,如果不設定就預設和minSdkVersion相同。這個屬性通知系統,已經針對這個目標版本測試過程式,系統不必使用相容模式來讓你的應用程式向前相容這個目標版本。 如果targetSdkVersion為19(對應為Android4.4),應用執行時,最高只能使用API 19的新特性。即使程式碼中使用了API 23的新特性,實際執行時,也不會使用該新特性; 可見targetSdkVersion這個屬性是在程式執行時期起作用的,系統根據這個屬性決定要不要以相容模式執行這個程式 例如,Android6.0系統增加了動態許可權機制,如果為了追時髦,盲目把你的targetSDKVersion設定為23(6.0),那麼在需要使用許可權的地方將會出現異常。為此,在你做好動態許可權申請之前,為保障APP正常執行,你需要將目標版本設定低於23。

(3)complieSdkVersion

compileSdkVersion僅僅是告訴Gradle使用哪個版本的SDK編譯應用,不會被包含到apk中,完全不影響應用的執行結果;雖然沒有影響但是

即 :compileSDKVersion是和編譯器打交道的,而minSDKVersion和targetSDKVersion是和系統打交道的。

2、如何升級app的動態許可權

首先需要了解動態許可權這個概念

2.1 動態許可權

這個是在android 6.0提出的,從該版本開始,使用者實在執行時而不是應用安裝時授予或者撤銷應用許可權,對應API23,這個版本最重要的變化就是許可權動態管理,任何未被授予許可權的邏輯都可能引起後續執行的崩潰,可以通過adb 工具從命令列獲取許可權

按組列出許可權和狀態:

$ adb shell pm list permissions -d -g

授予或撤銷一項或多項許可權:

$ adb shell pm [grant|revoke] ...

6.0之前的許可權在AndroidMenifest宣告之後即可獲取所有許可權,這樣會造成使用者會默默忍受一些不必要的許可權,特別是通訊錄,位置簡訊之類的比較敏感的許可權,在6.0之後我們只有在需要許可權的時候才會向使用者請求,使用者可以選擇拒絕,新的機制較好的保護的使用者的隱私。 谷歌將許可權分為兩大類 Normal Permissions 這類許可權一般不涉及使用者隱私是不需要使用者進行授權的,比如網路訪問、藍芽(詳細許可權見附錄) Dangenerous Permission 這類涉及到使用者隱私,需要使用者進行授權,比如讀取sdcard、開啟照相機、訪問通訊錄等,其中危險許可權是分組的,使用者之前如果已經選擇通過該組某個危險許可權,主要有以下幾組(詳細許可權見附錄)

  1. 身體感測器
  2. 日曆 攝像頭
  3. 通訊錄
  4. 地理位置
  5. 麥克風
  6. 電話
  7. 簡訊
  8. 儲存空間

2.2 許可權相關API

為了方便開發者實現許可權管理,谷歌給出下面4個API

關於動態許可權

一般許可權檢查需要以下三個步驟配合

1、檢查許可權是否授予

//Activity.java
> public int checkSelfPermission(permission) 
複製程式碼

這個是在ContextCompat類中用來判斷是否app已經獲取到某一個許可權的使用權。如果返回android.content.pm.PackageManager#PERMISSION_GRANTED,則說明app被授予許可權,如果返回android.content.pm.PackageManager#PERMISSION_DENIED 則說明許可權被禁止授予

2、申請許可權

//Activity.java
public final void requestPermissions( new String[permission1,permission2,...], requestCode)
複製程式碼

這個是在ActivityCompat類以及FragmentCompat類中用來申請許可權的方法

3、許可權回撥 使用者在系統彈窗裡面選擇以後結果會通過Activity的onRequestPermissionsResult 方法回撥

public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
{
    //繼續執行邏輯或者提示許可權獲取失敗
}
複製程式碼

這些方法可以封裝在BaseActvity中

4、是否顯示許可權對話方塊 這個是作為輔助類

public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity, @NonNull String permission) 這個是在ActivityCompat類以及FragmentCompat類中用來判斷是否顯示許可權詢問對話方塊。在許可權申請的過程中,如果使用者選擇允許許可權使用或者在拒絕的同時又勾選了不再詢問對話方塊,這2種情況下該api就會返回false,表示不再彈出許可權詢問對話方塊,其中授權彈窗是不支援自定義的

這個輔助函式十分重要,當動態許可權申請時候如果使用者如果使用者勾選了“不再詢問”,那麼在執行requestPermissions( )後,onRequestPermissionsResult( )會永遠返回PERMISSION_DENIED,這樣應用原本的操作將永遠無法執行,這是我們不希望看到的

關於動態許可權
谷歌還是給我們留了一手,就是 shouldShowRequestPermissionRationale,如果返回true,應用應該彈出dialog說明申請許可權的緣由比如下面這種

關於動態許可權

其中第一次申請許可權時候該函式預設返回false,無需告知使用者申請該許可權的理由;當第一次申請被拒絕,再次申請時該函式會返回true,並且彈出的系統dialog上會有一個選擇“不再詢問”,要是使用者勾選了該選項,則以後該函式一直會返回false。

2.3 如何適配

如果專案的targetSdkVersion < 23 執行在android 6.0及以上的系統上,會預設給與AndroidManifest.xml中申請的許可權,是不是這樣就萬事大吉了呢?顯然是想多了,如果使用者在應用的許可權頁面手動回收許可權,將會導致應用crash

關於動態許可權

雖然系統提醒了使用者,但是還是要取決使用者心情,因此穩妥的適配動態還是有必要的事情。

首先targetSdkVersion小於23的應用預設授予了所申請的所有許可權,所以如果你以前的APP設定的targetSdkVersion低於23,也能正常使用。等於或者大於23,則必須 request permission,否則會崩潰閃退。 下面將大致闡述一下升級動態許可權步驟

(1)宣告目標版本sdk 修改build.gradle中targetSdkVersion為23以上

(2)檢查許可權申請地方並request許可權 在我們需要使用到許可權的地方,檢查是否已經擁有許可權,比如讀寫外接SD卡許可權,我們在寫入之前檢車是否有許可權,沒有許可權則申請許可權

private void requestContactPermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_CONTACTS)
                != PackageManager.PERMISSION_GRANTED) {
            //申請 WRITE_CONTACTS 許可權
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_CONTACTS},
                    REQUEST_CODE_WRITE_CONTACTS);
        }
    } 
複製程式碼

(3)響應使用者許可權回撥

使用者可以選擇allow或者deny,可能deny。回撥onRequestPermissionsResult方法, 該方法類似於onActivityResult。如果是fragment,最好是使用父fragment,但不是使用ActivityCompat。建議使用getParentFragment().requestPermissions方法。

(4)根據requestCode和grandResult(授權結果)做相應處理

private void handleGrantResults(int requestCode, int[] grantResults) {
      if (requestCode == WRITE_EXTERNAL_STORAGE_REQUEST_CODE) {
          if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
              // Permission Granted 獲得許可權後執行xxx
          } else {
              // Permission Denied 拒絕後xx的操作。
          }
      }
  }
複製程式碼

3、後話

3.1、許可權申請如果使用者拒絕怎麼處理

如果使用者拒絕了核心許可權一次,下次再次申請會出現不再提醒的選項,要是選擇不再提醒,核心許可權不給獲取一些功能就無法使用,你可以選擇直接退出應用,或者可以優雅點,我們自己檢測一下是否拒絕了兩次或者以上,自己給個提示向使用者做一個互動

private void handleContactPermission() {
        if (Integer.parseInt(Build.VERSION.SDK)>=23) {
            int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.WRITE_CONTACTS);
            if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
                if (!shouldShowRequestPermissionRationale(Manifest.permission.WRITE_CONTACTS)) {
                    showMessageOKCancel("You need to allow access to Contacts",
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    requestContactPermission();//確定後申請許可權。
                                }
                            });
                    return;
                }
                requestContactPermission();//沒有許可權的話,申請。
            }
        }
複製程式碼

或者記錄使用者勾選了不再詢問 Google提供了一個非常好的思路,詳見EasyPermissions . EasyPermissions並沒有儲存上一次shouldShowRequestPermissionRationale( )的返回值,而是在申請許可權被拒後呼叫shouldShowRequestPermissionRationale( )方法,如果此時返回false則說明使用者勾選了“不再詢問”。

兩種方式都可以作為參考方式。

3.2 取消支援Apache http 客戶端

Android 6.0 版移除了對 Apache HTTP 客戶端的支援。如果您的應用使用該客戶端,並以 Android 2.3(API 級別 9)或更高版本為目標平臺,請改用HttpURLConnection 類。此 API 效率更高,因為它可以通過透明壓縮和響應快取減少網路使用,並可最大限度降低耗電量。要繼續使用 Apache HTTP API,您必須先在 build.gradle 檔案中宣告以下編譯時依賴項: android { useLibrary 'org.apache.http.legacy' }

3.3 升級到7.0 api 24及以上

對於面向 Android 7.0 的應用,Android 框架執行的 StrictMode API 政策禁止在您的應用外部公開 file:// URI。如果一項包含檔案 URI 的 intent 離開您的應用,則應用出現故障,並出現 FileUriExposedException 異常,常見的是訪問升級下載到內部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

Dangenerous Permission

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

###參考連結

公眾號小.jpg

相關文章