在最近的项目中需要对用户安装的程序点击的权限状态信息进行获取,当用户点击了禁止权限后,没有任何信息反馈,对于风控及安全类软件都无法进行管控。
在Android6.0之前,我看了好多的安全软件,比如360手机卫士,安全管家等类似的没有Root权限的软件是无法获取到动态权限信息的,360安全卫士要获取此类信息需要对手机进行root操作
在Android6.0之后对动态权限进行了加入
下面我对动态权限信息进行详细讲述:
1、概述:
Android6.0(API 23 )之前的应用的权限在安装时全部授予,运行时应用不再询问用户。在Android6.0或更高版本对权限进行了分类,对某些设计到用户隐私的权限可在运行时根据用户的需要动态授予。这样就不需要在安装时被强迫同意某些权限。
2、正常权限和危险权限:
Android系统权限分为几个保护级别。需要了解的两个最重要保护级别是
正常权限
和
危险权限
:
(1)正常权限:
涵盖应用需要访问其沙盒外部数据或资源,但对用户隐私或其他应用操作风险很小的区域。这些权限在应用安装时授予,运行时不再询问用户。例如: 网络访问、WIFI状态、音量设置等。完整的正常权限列表参考官网
正常权限
。
(2)危险权限:
涵盖应用需要涉及用户隐私信息的数据或资源,或者可能对用户存储的数据或其他应用的操作产生影响的区域。例如: 读取通讯录、读写存储器数据、获取用户位置等。如果应用声明需要这些危险权限,则必须在运行时明确告诉用户,让用户手动授予。
3、在运行时请求权限:
设备系统是在Android6.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)
当检测到应用没有指定的权限时,调用此方法向用户请求权限。调用此方法将弹出权限请求对话框询问用户“允许”或“拒绝”指定的权限。
PS:
- 权限参数传入的是数组,可以调用该方法一次请求多个权限;
-
传入的权限数组参数以单个具体权限为单位,但弹框询问用户授权时,
属于同一权限组的权限将自动合并询问授权一次
; - 请求的权限必须事先在 AndroidManifest.xml 中有声明,否则调用此方法请求时,将不弹框,而是直接返回“拒绝”的结果;
- 第一次请求权限时,用户点击了“拒绝”,第二次再请求该权限时,对话框将出现“不再询问”复选框,如果用户勾选了“不再询问”并点击了“拒绝”,则之后再请求此权限组时将不弹框,而是直接返回“拒绝”的结果。
(3)处理结果:
请求权限的结果返回和接收一个Activity的返回类似,重写FragmentActivity或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) {
- // ...
- }
下面提供demo代码
1、首先在AndroidManifest.xml中声明权限:
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.localuser.testandroid">
- <!-- 声明所有需要的权限(包括普通权限和危险权限) -->
- <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"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.localuser.testandroid">
- <!-- 声明所有需要的权限(包括普通权限和危险权限) -->
- <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>
3、MainActivity文件:
- package com.example.localuser.testandroid;
-
- 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;
- import android.util.Log;
-
- /**
- * 一键备份通讯录
- *
- * @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
- Log.i("permission权限:"+permission,"禁止");
- return false;
- }else{
- Log.i("permission权限:"+permission,"允许");
- }
- }
- 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();
- }
-
- }
- 这个程序是用AndroidStudio开发的,如果有需要源码的,可以找我要。