一.背景知识
Android 6.0(API 23)开始,应用运行时用户向其授予权限,权限不再是在应用安装时授予。 Android 6.0之前,所有需要的权限都在应用安装的时候被系统授予。应用使用的时候不需要对权限做任何的处理。由于用户在安装的时候一般都会对这些权限用在了什么地方做深入的考量,往往默认授予这些权限,从而导致了诸多的安全风险。Android 6.0后的动态权限虽然牺牲了部分便利性,得到的了更高的系统安全。
在应用开发中,主要涉及到对系统危险权限的动态处理,这些危险权限可以划分为9类,详情见下图:
网上有大量讲解Android 6.0动态权限的博文,但是许多博文要只是实现了权限的检测,有的博文也实现了权限的请求,但是并没有做多权限的适配,本文重点解决多权限的同时检测与请求问题,为一些实用的权限请求场景做更多的兼容。
二.实现思路
1.实现一个名为PermissionsUtils的API,专门负责对权限的检测与请求。主要适配多权限的同时检测,如果某些权限是拒绝但允许询问状态的话就对这些权限进行统一的请求处理。注意,需要请求的权限必须在AndroidManifest.xml中注册。
2.在MainActivity实现两个按钮的点击事件,分别触发单一权限与多权限的监测与请求操作,并对结果进行展示。
3.PermisionsConst是一个常量类,分别定义了需要检测与请求的单权限与多权限数组。
4.PermisionsBean是为方便参数传递而定义的一个Bean类,结合PermissionsUtils的相关方法代码就不难理解了。
三.主要代码
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:id="@+id/btn_request_one"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/request_one"
android:layout_margin="8dp"
android:textColor="@color/colorWhite"
android:background="@color/colorPrimaryDark"/>
<Button
android:id="@+id/btn_request_multi"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/request_multi"
android:layout_margin="8dp"
android:textColor="@color/colorWhite"
android:background="@color/colorPrimaryDark"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorGray"
android:layout_marginTop="8dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="已经授予的权限"
android:textSize="18sp"
android:layout_marginLeft="12dp" />
<TextView
android:id="@+id/tv_granted"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorGray"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="拒绝授予的权限(还可以询问)"
android:textSize="18sp"
android:layout_marginLeft="12dp" />
<TextView
android:id="@+id/tv_denied_can_ask"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorGray"
android:layout_marginTop="8dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="拒绝授予的权限(不再询问)"
android:textSize="18sp"
android:layout_marginLeft="12dp" />
<TextView
android:id="@+id/tv_denied_not_ask"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorGray"
android:layout_marginTop="8dp"/>
</LinearLayout>
PermissionsUtils.java
package com.example.leidong.permissionsrequest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import com.example.leidong.permissionsrequest.interfaces.OnPermissionsCheckListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
/**
* Created by Lei Dong on 2018/5/28.
*/
public class PermissionsUtils {
private static volatile int mRequestCode = 0;
private static final int MAX_REQUEST_CODE = Short.MAX_VALUE;
private static final int PERMISSION_GRANTED_STATE = PackageManager.PERMISSION_GRANTED;
private static final int PERMISSION_DENIED_NOT_ASK_STATE = PackageManager.PERMISSION_DENIED;
private static final int PERMISSION_DENIED_CAN_ASK_STATE = -2;
/**
* Key:requestCode
* Value:PermisisonBean对象,装载了全部的权限信息和回调接口
*/
private static HashMap<Integer, PermissionsBean> mPermissionsBeanMap = new HashMap<>();
/**
* 装载全部权限信息的Map
*/
private static HashMap<String, Integer> permissionsMap = new HashMap<>();
/**
* 申请多条权限
*
* @param context
* @param permissionsMap
* @param onPermissionsCheckListener
*/
private static void requestMultiPermissions(Context context, HashMap<String, Integer> permissionsMap, OnPermissionsCheckListener onPermissionsCheckListener) {
assert permissionsMap != null;
ArrayList<String> grantedPermissionsList = new ArrayList<>();
ArrayList<String> deniedPermissionsCanAskList = new ArrayList<>();
ArrayList<String> deniedPermissionsNotAskList = new ArrayList<>();
grantedPermissionsList = obtainPermissionsByState(permissionsMap, PERMISSION_GRANTED_STATE);
deniedPermissionsCanAskList = obtainPermissionsByState(permissionsMap, PERMISSION_DENIED_CAN_ASK_STATE);
deniedPermissionsNotAskList = obtainPermissionsByState(permissionsMap, PERMISSION_DENIED_NOT_ASK_STATE);
// 如果存在拒绝但是可再次询问的权限
if(deniedPermissionsCanAskList.size() > 0){
mRequestCode = generateRequestCode();
PermissionsBean permissionsBean = new PermissionsBean();
permissionsBean.setPermissionsMap(permissionsMap);
permissionsBean.setOnPermissionsCheckListener(onPermissionsCheckListener);
mPermissionsBeanMap.put(mRequestCode, permissionsBean);
ActivityCompat.requestPermissions((Activity) context, deniedPermissionsCanAskList.toArray(new String[deniedPermissionsCanAskList.size()]), mRequestCode);
}
// 如果不存在拒绝但是可再次询问的权限
else{
onPermissionsCheckListener.onResult(grantedPermissionsList, deniedPermissionsCanAskList, deniedPermissionsNotAskList);
}
}
/**
* 某权限是否可以再次请求
*
* @param mContext
* @param permission
* @return
*/
private static boolean shouldShowRequestPermissionRationale(Activity mContext, String permission) {
return ActivityCompat.shouldShowRequestPermissionRationale(mContext, permission);
}
/**
* 产生权限请求码
*
* @return
*/
private static int generateRequestCode() {
Random random = new Random();
return random.nextInt(MAX_REQUEST_CODE);
}
/**
* 检查多条权限,如果不具备就请求
*
* @param context
* @param permissions
* @param onPermissionsCheckListener
*/
public static void checkPermissions(Activity context, String[] permissions, OnPermissionsCheckListener onPermissionsCheckListener) {
for (String permission : permissions) {
permissionsMap.put(permission, PERMISSION_GRANTED_STATE);
}
ArrayList<String> grantedPermissionsList = new ArrayList<>();
ArrayList<String> deniedPermissionsCanAskList = new ArrayList<>();
ArrayList<String> deniedPermissionsNotAskList = new ArrayList<>();
if(permissions.length == 0){
onPermissionsCheckListener.onResult(grantedPermissionsList, deniedPermissionsCanAskList, deniedPermissionsNotAskList);
}
else if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT){
// API > 19 时,默认权限都已经获得
grantedPermissionsList = obtainPermissionsByState(permissionsMap, PERMISSION_GRANTED_STATE);
deniedPermissionsCanAskList = obtainPermissionsByState(permissionsMap, PERMISSION_DENIED_CAN_ASK_STATE);
deniedPermissionsNotAskList = obtainPermissionsByState(permissionsMap, PERMISSION_DENIED_NOT_ASK_STATE);
onPermissionsCheckListener.onResult(grantedPermissionsList, deniedPermissionsCanAskList, deniedPermissionsNotAskList);
}
else if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M){
// API >= 19 且 API< 23 时,权限只有允许与拒绝不再询问两种
for(String permission : permissions){
if(permissionsMap.get(permission) != PERMISSION_GRANTED_STATE){
permissionsMap.put(permission, PERMISSION_DENIED_NOT_ASK_STATE);
}
}
grantedPermissionsList = obtainPermissionsByState(permissionsMap, PERMISSION_GRANTED_STATE);
deniedPermissionsCanAskList = obtainPermissionsByState(permissionsMap, PERMISSION_DENIED_CAN_ASK_STATE);
deniedPermissionsNotAskList = obtainPermissionsByState(permissionsMap, PERMISSION_DENIED_NOT_ASK_STATE);
onPermissionsCheckListener.onResult(grantedPermissionsList, deniedPermissionsCanAskList, deniedPermissionsNotAskList);
}
else {
// API >= 23 的情况 使用系统API进行权限判断
for (String permission : permissions) {
int permissionState = ActivityCompat.checkSelfPermission(context, permission);
if (permissionState != PERMISSION_GRANTED_STATE
&& shouldShowRequestPermissionRationale(context, permission)) {
permissionsMap.put(permission, PERMISSION_DENIED_CAN_ASK_STATE);
}
else if(permissionState != PERMISSION_GRANTED_STATE
&& !shouldShowRequestPermissionRationale(context, permission)){
permissionsMap.put(permission, PERMISSION_DENIED_NOT_ASK_STATE);
}
}
}
requestMultiPermissions(context, permissionsMap, onPermissionsCheckListener);
}
/**
* 根据权限状态码填充对应权限到List中
*
* @param permissionsMap
* @param permissionState
* @return
*/
private static ArrayList<String> obtainPermissionsByState(HashMap<String, Integer> permissionsMap, int permissionState) {
ArrayList<String> permissionsList = new ArrayList<>();
for (String permission : permissionsMap.keySet()) {
if(permissionsMap.get(permission) == permissionState){
permissionsList.add(permission);
}
}
return permissionsList;
}
public static void onRequestPermissionsResult(Activity context, int requestCode, String[] permissions) {
PermissionsBean permissionsBean = mPermissionsBeanMap.get(requestCode);
OnPermissionsCheckListener onPermissionsCheckListener = permissionsBean.getOnPermissionsCheckListener();
HashMap<String, Integer> permissionsMap = permissionsBean.getPermissionsMap();
ArrayList<String> grantedPermissionsList = new ArrayList<>();
ArrayList<String> deniedPermissionsCanAskList = new ArrayList<>();
ArrayList<String> deniedPermissionsNotAskList = new ArrayList<>();
if(onPermissionsCheckListener != null){
for(String permission : permissionsMap.keySet()){
int permissionState = ContextCompat.checkSelfPermission(context, permission);
if(permissionState == PERMISSION_GRANTED_STATE){
permissionsMap.put(permission, PERMISSION_GRANTED_STATE);
}
else if(shouldShowRequestPermissionRationale(context, permission)){
permissionsMap.put(permission, PERMISSION_DENIED_CAN_ASK_STATE);
}
else{
permissionsMap.put(permission, PERMISSION_DENIED_NOT_ASK_STATE);
}
}
}
grantedPermissionsList = obtainPermissionsByState(permissionsMap, PERMISSION_GRANTED_STATE);
deniedPermissionsCanAskList = obtainPermissionsByState(permissionsMap, PERMISSION_DENIED_CAN_ASK_STATE);
deniedPermissionsNotAskList = obtainPermissionsByState(permissionsMap, PERMISSION_DENIED_NOT_ASK_STATE);
permissionsMap.remove(requestCode);
assert onPermissionsCheckListener != null;
onPermissionsCheckListener.onResult(grantedPermissionsList, deniedPermissionsCanAskList, deniedPermissionsNotAskList);
}
}
MainActivity.java
package com.example.leidong.permissionsrequest;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.example.leidong.permissionsrequest.interfaces.IBaseActivity;
import com.example.leidong.permissionsrequest.interfaces.OnPermissionsCheckListener;
import java.util.ArrayList;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
/**
* Created by Lei Dong on 2018/5/28.
*/
public class MainActivity extends AppCompatActivity implements IBaseActivity {
private static final String TAG = MainActivity.class.getName();
@BindView(R.id.btn_request_one)
Button mBtnRequestOne;
@BindView(R.id.btn_request_multi)
Button mBtnRequestMulti;
@BindView(R.id.tv_granted)
TextView mTvGranted;
@BindView(R.id.tv_denied_can_ask)
TextView mTvDeniedCanAsk;
@BindView(R.id.tv_denied_not_ask)
TextView mTvDeniedNotAsk;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
initWidgets();
initActions();
}
@Override
public void initWidgets() {
clearAllTextViews();
}
private void clearAllTextViews() {
mTvGranted.setText(null);
mTvDeniedCanAsk.setText(null);
mTvDeniedNotAsk.setText(null);
}
@Override
public void initActions() {
}
@OnClick({R.id.btn_request_one, R.id.btn_request_multi})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.btn_request_one:
clickRequestOnePermissionBtn();
break;
case R.id.btn_request_multi:
clickRequestMultiPermisisonsBtn();
break;
}
}
private void clickRequestMultiPermisisonsBtn() {
PermissionsUtils.checkPermissions(MainActivity.this, PermissionsConst.PERMISSIONS, new OnPermissionsCheckListener() {
@Override
public void onResult(ArrayList<String> grantedPermissions, ArrayList<String> deniedCanAskPermissions, ArrayList<String> deniedNotAskPermissions) {
clearAllTextViews();
// 已经获取的权限展示
if(grantedPermissions.size() > 0){
StringBuilder s = new StringBuilder();
for (String permission : grantedPermissions) {
s.append(permission).append(" --->>> ");
}
mTvGranted.setText(s.toString());
}
// 未获取但可以再次询问的权限的展示
if(deniedCanAskPermissions.size() > 0){
StringBuilder s = new StringBuilder();
for (String permission : deniedCanAskPermissions) {
s.append(permission).append(" --->>> ");
}
mTvDeniedCanAsk.setText(s.toString());
}
// 未获取且不再询问的权限的展示
if(deniedNotAskPermissions.size() > 0){
StringBuilder s = new StringBuilder();
for (String permission : deniedNotAskPermissions) {
s.append(permission).append(" --->>> ");
}
mTvDeniedNotAsk.setText(s.toString());
}
}
});
}
private void clickRequestOnePermissionBtn() {
PermissionsUtils.checkPermissions(MainActivity.this, PermissionsConst.PERMISSION, new OnPermissionsCheckListener() {
@Override
public void onResult(ArrayList<String> grantedPermissions, ArrayList<String> deniedCanAskPermissions, ArrayList<String> deniedNotAskPermissions) {
clearAllTextViews();
if(grantedPermissions.size() > 0){
mTvGranted.setText(grantedPermissions.get(0));
}
else if(deniedCanAskPermissions.size() > 0){
mTvDeniedCanAsk.setText(deniedCanAskPermissions.get(0));
}
else{
mTvDeniedNotAsk.setText(deniedNotAskPermissions.get(0));
}
}
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(permissions.length > 0){
PermissionsUtils.onRequestPermissionsResult(MainActivity.this, requestCode, permissions);
}
}
}
PermissionsConst.java
package com.example.leidong.permissionsrequest;
import android.Manifest;
/**
* Created by Lei Dong on 2018/5/28.
*/
public class PermissionsConst {
/**
* 请求的单项权限
*/
public static final String[] PERMISSION = {
android.Manifest.permission.READ_SMS
};
/**
* 请求的多项权限
*/
public static final String[] PERMISSIONS = {
Manifest.permission.READ_CALENDAR,
Manifest.permission.CAMERA,
Manifest.permission.READ_CONTACTS,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.BODY_SENSORS,
Manifest.permission.SEND_SMS,
Manifest.permission.READ_EXTERNAL_STORAGE
};
}
PermissionsBean.java
package com.example.leidong.permissionsrequest;
import com.example.leidong.permissionsrequest.interfaces.OnPermissionsCheckListener;
import java.util.HashMap;
/**
* Created by Lei Dong on 2018/5/28.
*/
public class PermissionsBean {
private OnPermissionsCheckListener onPermissionsCheckListener;
private HashMap<String, Integer> permissionsMap;
public PermissionsBean() {
}
public PermissionsBean(OnPermissionsCheckListener onPermissionsCheckListener, HashMap<String, Integer> permissionsMap) {
this.onPermissionsCheckListener = onPermissionsCheckListener;
this.permissionsMap = permissionsMap;
}
public OnPermissionsCheckListener getOnPermissionsCheckListener() {
return onPermissionsCheckListener;
}
public void setOnPermissionsCheckListener(OnPermissionsCheckListener onPermissionsCheckListener) {
this.onPermissionsCheckListener = onPermissionsCheckListener;
}
public HashMap<String, Integer> getPermissionsMap() {
return permissionsMap;
}
public void setPermissionsMap(HashMap<String, Integer> permissionsMap) {
this.permissionsMap = permissionsMap;
}
}
OnPermissionsCheckListener.java
package com.example.leidong.permissionsrequest.interfaces;
import java.util.ArrayList;
/**
* Created by Lei Dong on 2018/5/28.
*/
public interface OnPermissionsCheckListener {
void onResult(ArrayList<String> grantedPermissions, ArrayList<String> deniedCanAskPermissions, ArrayList<String> deniedNotAskPermissions);
}
四.运行结果
五.下载地址