標籤:系統 ant normal length 為什麼 http uil public lang
Android 6.0 動態許可權申請 1. 概述
Android 6.0 (API 23) 之前應用的許可權在安裝時全部授予,運行時應用不再需要詢問使用者。在 Android 6.0 或更高版本對許可權進行了分類,對某些涉及到使用者隱私的許可權可在運行時根據使用者的需要動態授予。這樣就不需要在安裝時被強迫同意某些許可權。
2. 正常許可權 和 危險許可權
Android系統許可權分為幾個保護層級。需要瞭解的兩個最重要保護層級是 正常許可權 和 危險許可權:
(1)正常許可權:
涵蓋應用需要訪問其沙箱外部資料或資源,但對使用者隱私或其他應用操作風險很小的地區。這些許可權在應用安裝時授予,運行時不再詢問使用者。例如: 網路訪問、WIFI狀態、音量設定等。完整的正常許可權列表參考官網 正常許可權。
(2)危險許可權:
涵蓋應用需要涉及使用者隱私資訊的資料或資源,或者可能對使用者儲存的資料或其他應用的操作產生影響的地區。例如: 讀取通訊錄、讀寫儲存空間資料、擷取使用者位置等。如果應用聲明需要這些危險許可權,則必須在運行時明確告訴使用者,讓使用者手動授予。
3. 許可權組
Android系統對所有的危險許可權進行了分組,稱為 許可權組 。屬於同一組的危險許可權將自動合并授予,使用者授予應用某個許可權組的許可權,則應用將獲得該許可權組下的所有許可權(前提是相關許可權在 AndroidManifest.xml 中有聲明)。
危險許可權 和 許可權組 列表如下:
PS: 在 AndroidManifest.xml 聲明過的危險許可權對應的許可權組可以在系統 “設定” -> “應用” -> “應用資訊” -> “許可權” 中查看,可以手動授權和取消授權。
許可權組和許可權在Android代碼中以 字串常量 來表示,分別定義在以下兩個 靜態內部類 的欄位中:
- android.Manifest.permission_group(許可權組):
Manifest.permission_group.CALENDAR
Manifest.permission_group.STORAGE
......
- android.Manifest.permission(許可權):
Manifest.permission.READ_CALENDAR
Manifest.permission.READ_EXTERNAL_STORAGE
......
4. 在運行時請求許可權
裝置系統是 Android 6.0 (API 23) 或更高版本,並且應用的 targetSdkVersion 是 23 或更高版本,則針對在 AndroidManifest.xml 中聲明的危險許可權,在運行時還需要動態請求使用者授權。
動態許可權請求相關操作的API封裝在在android.support.v4
包中,發起請求許可權的Activity
需要直接或間接繼承android.support.v4.app.FragmentActivity
。
PS: 也可以在直接或間接繼承 android.support.v4.app.Fragment
的 Fragment
中發起許可權請求。
代碼步驟中主要包含以下幾個方法:
(1)檢查許可權
// 檢查許可權ContextCompat.checkSelfPermission(Context context, String permission)
傳回值(android.content.pm.PackageManager
中的常量):
- 有許可權:
PackageManager.PERMISSION_GRANTED
- 無許可權:
PackageManager.PERMISSION_DENIED
當應用需要用到危險許可權時,在執行許可權相關代碼前,使用該方法判斷是否擁有指定的許可權。有許可權,則繼續執行設計需要許可權的代碼;無許可權,則向使用者請求授予許可權。
(2)解釋許可權
// 解釋許可權ActivityCompat.shouldShowRequestPermissionRationale(Activity activity, String permission)
判斷是否有必要向使用者解釋為什麼要這項許可權。如果應用第一次請求過此許可權,但是被使用者拒絕了,則之後調用該方法將返回 true,此時就有必要向使用者詳細說明需要此許可權的原因(個人認為此方法是可選的)。
PS: 如果應用第一次請求此許可權時被使用者拒絕,第二次再請求此許可權時,使用者勾選了許可權請求對話方塊的“不再詢問”,則此方法返回 false。如果裝置規範禁止應用擁有該許可權,此方法也返回 false。
(3)請求許可權
// 請求許可權ActivityCompat.requestPermissions(Activity activity, String[] permissions, int requestCode)
當檢測到應用沒有指定的許可權時,調用此方法向使用者請求許可權。調用此方法將彈出許可權請求對話方塊詢問使用者 “允許” 或 “拒絕” 指定的許可權。
PS:
- 許可權參數傳入的是數組,可以調用該方法一次請求多個許可權;
- 傳入的許可權數組參數以單個具體許可權為單位,但彈框詢問使用者授權時,屬於同一許可權組的許可權將自動合并詢問授權一次;
- 請求的許可權必須事先在 AndroidManifest.xml 中有聲明,否則調用此方法請求時,將不彈框,而是直接返回“拒絕”的結果;
- 第一次請求許可權時,使用者點擊了“拒絕”,第二次再請求該許可權時,對話方塊將出現“不再詢問”複選框,如果使用者勾選了“不再詢問”並點擊了“拒絕”,則之後再請求此許可權組時將不彈框,而是直接返回“拒絕”的結果。
(4)處理結果
請求許可權的結果返回和接收一個Activity的返回類似,重寫 FragmentActivity
或 (v4) Fragment
中的 onRequestPermissionsResult(...)
方法。
/** * 處理許可權請求結果 * * @param requestCode * 請求許可權時傳入的請求碼,用於區別是哪一次請求的 * * @param permissions * 所請求的所有許可權的數組 * * @param grantResults * 許可權授予結果,和 permissions 數組參數中的許可權一一對應,元素值為兩種情況,如下: * 授予: PackageManager.PERMISSION_GRANTED * 拒絕: PackageManager.PERMISSION_DENIED */
@Overridepublic void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { // ...}
5. 代碼示範
功能很簡單,如下:
- 點擊一個按鈕,如果有相應許可權就進行備份通訊錄操作;
- 如果沒有相應的許可權,則向使用者申請許可權;
- 如果使用者授權通過,則繼續進行備份通訊錄操作;
- 如果使用者拒絕授權,則彈出對話方塊引導使用者跳轉到應用許可權管理介面手動授權。
效果大致如下:
部分檔案代碼:
1、首先在 AndroidManifest.xml 中聲明許可權
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.xiets.demoapp"> <!-- 聲明所有需要的許可權(包括普通許可權和危險許可權) --> <uses-permission android:name="android.permission.READ_CONTACTS"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <application android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application></manifest>
2、布局檔案 activity_main.xml
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="10dp"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="click" android:textSize="20sp" android:text="備份通訊錄" /></RelativeLayout>
3、MainActivity
package com.xiets.demoapp;import android.Manifest;import android.content.DialogInterface;import android.content.Intent;import android.content.pm.PackageManager;import android.net.Uri;import android.os.Bundle;import android.provider.Settings;import android.support.annotation.NonNull;import android.support.v4.app.ActivityCompat;import android.support.v4.content.ContextCompat;import android.support.v7.app.AlertDialog;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.Toast;/** * 一鍵備份通訊錄 * * @author xietansheng */public class MainActivity extends AppCompatActivity { private static final int MY_PERMISSION_REQUEST_CODE = 10000; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } /** * 點擊按鈕,將通訊錄備份儲存到外部儲存空間備。 * * 需要3個許可權(都是危險許可權): * 1. 讀取通訊錄許可權; * 2. 讀取外部儲存空間許可權; * 3. 寫入外部儲存空間許可權. */ public void click(View view) { /** * 第 1 步: 檢查是否有相應的許可權 */ boolean isAllGranted = checkPermissionAllGranted( new String[] { Manifest.permission.READ_CONTACTS, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE } ); // 如果這3個許可權全都擁有, 則直接執行備份代碼 if (isAllGranted) { doBackup(); return; } /** * 第 2 步: 請求許可權 */ // 一次請求多個許可權, 如果其他有許可權是已經授予的將會自動忽略掉 ActivityCompat.requestPermissions( this, new String[] { Manifest.permission.READ_CONTACTS, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }, MY_PERMISSION_REQUEST_CODE ); } /** * 檢查是否擁有指定的所有許可權 */ private boolean checkPermissionAllGranted(String[] permissions) { for (String permission : permissions) { if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { // 只要有一個許可權沒有被授予, 則直接返回 false return false; } } return true; } /** * 第 3 步: 申請許可權結果返回處理 */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == MY_PERMISSION_REQUEST_CODE) { boolean isAllGranted = true; // 判斷是否所有的許可權都已經授予了 for (int grant : grantResults) { if (grant != PackageManager.PERMISSION_GRANTED) { isAllGranted = false; break; } } if (isAllGranted) { // 如果所有的許可權都授予了, 則執行備份代碼 doBackup(); } else { // 彈出對話方塊告訴使用者需要許可權的原因, 並引導使用者去應用許可權管理中手動開啟許可權按鈕 openAppDetails(); } } } /** * 第 4 步: 備份通訊錄操作 */ private void doBackup() { // 本文主旨是講解如果動態申請許可權, 具體備份代碼不再展示, 就假裝備份一下 Toast.makeText(this, "正在備份通訊錄...", Toast.LENGTH_SHORT).show(); } /** * 開啟 APP 的詳情設定 */ private void openAppDetails() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage("備份通訊錄需要訪問 “通訊錄” 和 “外部儲存空間”,請到 “應用資訊 -> 許可權” 中授予!"); builder.setPositiveButton("去手動授權", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(); intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setData(Uri.parse("package:" + getPackageName())); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); startActivity(intent); } }); builder.setNegativeButton("取消", null); builder.show(); }}
資料來源:54315674
android 6.0之後動態擷取許可權