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*/ @Override public 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"/><applicationandroid: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"><Buttonandroid: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;@Overrideprotected 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) {// 只要有一個權限沒有被授予, 則直接返回 falsereturn false;}}return true;}/*** 第 3 步: 申請權限結果返回處理*/@Overridepublic 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() {@Overridepublic 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();}}?
轉載于:https://www.cnblogs.com/zhujiabin/p/9305521.html
總結
以上是生活随笔為你收集整理的Android 6.0 动态权限申请的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Steam获史诗级更新:游戏“喜加一”终
- 下一篇: 理想ONE高速追尾工程车辆!官方回应:事