[TOC]
动态权限适配方案
1. 权限管理的更改
- Android 6.0之前,权限的申请在清单文件中注册即可(用户安装App时会列出安装的App的访问权限,而且只有安装时会出现一次)
- Android 6.0开始,权限除了在清单文件中注册,部分危险权限需要在使用时主动向用户发起确认
6.0之前,App如果申请了各种隐私权限并在安装的时候被忽略而允许, 就可以在用户毫不知晓的情况下访问权限内的所有东西,比如用户的通信信息、用户的位置等,这会侵犯用户的隐私。这项改动避免了app滥用权限,也消除了流氓应用(不给权限不让用)的存在
2. 危险权限的申请方案
一般开发中权限的使用有这么两种情况:
- 需要读取设备信息、读写存储信息权限,且在应用运行立刻需要的,不立即授予无法正常使用;
- 需要相机、录音等权限,在应用使用到该功能时才需要的,不立即授予也能正常使用。
因此可以得出申请方案:在应用启动时,对所有需要的危险权限进行权限申请,立即需要的权限未被授予的情况下,弹出询问,一再拒绝的情况下强制跳应用权限设置页面要求开启,不授予不能正常使用;其他非立即需要权限启动时询问被拒绝后不在询问,在使用到该功能时要求授予,不授予不能正常使用该功能
2.1 基本的权限申请
最基本的权限申请使用(官方做法在此处)
-
权限检查
通过checkSelfPermission(this, Manifest.permission.CAMERA)拿到当前申请权限的授予状态 -
权限申请
通过ActivityCompat.requestPermissions(final Activity activity,final String[] permissions, final int requestCode)进行申请权限 -
申请结果
通过onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)接收申请结果 -
申请被拒绝处理
通过ActivityCompat.shouldShowRequestPermissionRationale(Activity activity,String permission)再次申请,该方法返回false时表明本次申请权限被禁止询问申请,此时需要进行手动开启 -
应用设置页手动允许
各种方案跳转
If your application is targeting an API level before 23 (Android M) then both:ContextCompat.CheckSelfPermission and Context.checkSelfPermission doesn't work and always returns 0 (PERMISSION_GRANTED). Even if you run the application on Android 6.0 (API 23).
在targetSdkVersion小于23(Android M)的时候,ContextCompat.CheckSelfPermission 和Context.checkSelfPermission方法都不能正常工作并且始终返0(PERMISSION_GRANTED),即使你的应用运行在Android6.0(API 23)的设备上。
当编译targetSDKVersion < 23时使用
ContextCompat.checkSelfPermission and Context.checkSelfPermission将会不起作用
需要使用
PermissionChecker.checkSelfPermission
同样targetSDKVersion >= 23时PermissionChecker.checkSelfPermission也会无效,
需要使用ContextCompat.checkSelfPermission
2.2 权限申请方案封装
将申请方法全部封装到基类PermissionActivity中,子类通过调用父类的方法进行权限的申请判断
requestPermission(Manifest.permission.CAMERA, "需要拍照(此处解释下为啥需要这个权限)", new PermissionActivity.PermissionCallback() {
@Override
public void granted() {
Toast.makeText(TestPermissionActivity.this,"获取成功",Toast.LENGTH_SHORT).show();
}
@Override
public void denied() {
Toast.makeText(TestPermissionActivity.this,"获取失败",Toast.LENGTH_SHORT).show();
}
@Override
public void waitUserInitiativeSet() {
Toast.makeText(TestPermissionActivity.this,"用户设置中",Toast.LENGTH_SHORT).show();
}
});
PermissionActivity
package com.sj.d_1_adaptiveversion.version_6;
import android.Manifest;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.PermissionChecker;
public class PermissionActivity extends AppCompatActivity {
private int permissionResultCode = 1;
private PermissionCallback permissionCallback;
/**
* 权限申请入口
* @param permission 权限名
* @param permissionReason 获取理由
* @param permissionCallback 结果回调
*/
public void requestPermission(final String permission, String permissionReason, PermissionCallback permissionCallback) {
this.permissionCallback=permissionCallback;
if (!hasPermission(permission)) {
if (needExplainPermissionReason(permission)) {
new AlertDialog.Builder(this)
.setTitle("")
.setMessage(permissionReason)
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
requestPermission(permission);
}
})
.show();
} else {
requestPermission(permission);
}
}else {
if (this.permissionCallback != null) {
this.permissionCallback.granted();
}
}
}
/**
* 申请权限的有无状态
* @param permission
* @return
*/
private boolean hasPermission(String permission) {
int targetSdkVersion = getApplicationInfo().targetSdkVersion;
if (targetSdkVersion >= Build.VERSION_CODES.M) {
return ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED;
} else {
return PermissionChecker.checkSelfPermission(this, permission) == PermissionChecker.PERMISSION_GRANTED;
}
}
/**
* 是否需要解释该权限
* 权限需要解释的条件:上一次请求被拒绝,且没有点击不再询问
* @param permission
* @return true 需要解释后再次进行询问;false:不需询问有两种情况 用户禁止询问了,从未询问过
*/
private boolean needExplainPermissionReason(String permission) {
return ActivityCompat.shouldShowRequestPermissionRationale(this, permission);
}
/**
* 申请权限
* @param permission
*/
private void requestPermission(String permission) {
ActivityCompat.requestPermissions(PermissionActivity.this, new String[]{permission}, permissionResultCode);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull final String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == permissionResultCode) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//权限通过
} else {
if (!needExplainPermissionReason(Manifest.permission.CAMERA)) {
//解释理由 跳转去应用信息页设置
new AlertDialog.Builder(this)
.setTitle("提示")
.setMessage("主动询问已被禁止,需要到应用权限配置页面主动授予是否允许?")
.setPositiveButton("跳转", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
toAppPermissionSet();
if (permissionCallback != null) {
permissionCallback.waitUserInitiativeSet();
}
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
if (permissionCallback != null) {
permissionCallback.denied();
}
}
})
.show();
}
}
return;
}
}
/**
* 跳转应用权限配置页面
*/
private void toAppPermissionSet() {
AppPermissionSettingPageUtils.toPermissionSettingPage(this);
}
public interface PermissionCallback{
/**
* 获取权限成功
*/
void granted();
/**
* 获取权限失败
*/
void denied();
/**
* 用户跳转配置页配置中
*/
void waitUserInitiativeSet();
}
}
3 危险权限清单
Dangerous Permissions:
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