写这一系列的文章是因为刚好最近有个项目的targetversion需要升级到26,而在这之前是21,所以需要一下子适配6.0,7.0,8.0,顺便汇总一波需要注意的一些点,由于篇幅较长,因此分为3部分
本文主要是针对Android6.0需要适配的一些方法和注意事项,在Android6.0之前,我们在AndroidMainfest.xml里面声明的权限,都是在用户安装的时候就提示用户授予,然后就可以开始"为所欲为”(邪恶脸),在Android6.0之后,Google推出了运行时权限,一听名字就大概会晤其意了,也就是用户在运行我们的应用程序的过程中需要一些权限的时候需要动态弹出提示让用户手动点击授予,才可拥有该权限,且用户也可以在系统设置中管理每个App的权限,这样可以更方便用户控制App的功能使用。
Google在6.0将系统权限分为了普通权限和危险权限
普通权限只是一些对用户影响不大的权限,它们跟以前一样只要在AndroidMainfest.xml中声明即可直接使用。
危险权限是一些涉及用户隐私或系统安全的权限,这一类在AndroidMainfest.xml中声明也暂时无法使用,需要用户手动授予。
Permission Group | Permissions |
android.permission-group.CALENDAR | android.permission.READ_CALENDAR android.permission.WRITE_CALENDAR |
android.permission-group.CAMERA | android.permission.CAMERA |
android.permission-group.CONTACTS | android.permission.READ_CONTACTS android.permission.WRITE_CONTACTS android.permission.GET_ACCOUNTS |
android.permission-group.LOCATION | android.permission.ACCESS_FINE_LOCATION android.permission.ACCESS_COARSE_LOCATION |
android.permission-group.MICROPHONE | android.permission.RECORD_AUDIO |
android.permission-group.PHONE | android.permission.READ_PHONE_STATE android.permission.CALL_PHONE android.permission.READ_CALL_LOG android.permission.WRITE_CALL_LOG com.android.voicemail.permission.ADD_VOICEMAIL android.permission.USE_SIP android.permission.PROCESS_OUTGOING_CALLS |
android.permission-group.SENSORS | android.permission.BODY_SENSORS |
android.permission-group.SMS | android.permission.SEND_SMS android.permission.RECEIVE_SMS android.permission.READ_SMS android.permission.RECEIVE_WAP_PUSH android.permission.RECEIVE_MMS android.permission.READ_CELL_BROADCASTS |
android.permission-group.STORAGE | android.permission.READ_EXTERNAL_STORAGE android.permission.WRITE_EXTERNAL_STORAGE |
可以看出,Google将危险权限归为了好几个组,即所谓的权限组,那么权限组是什么概念呢,一旦你成功申请到这个组里面的某一个危险权限,这个组的其他权限你也能同时拥有,即实际上不需每个权限都去一一请求用户同意,只需要同组中的一个申请成功即可。
Android为我们提供了请求权限的api, ActivityCompat.requestPermission(Activity activity, String[] permissions, int requestCode)permissions可以传入我们需要申请的权限,传入多个就会请求多个,requestCode就类似于平时写onActivityResult()的套路一样,即方便到时候用户同意或拒绝之后的回调中类标识是哪一次申请。
用户操作回调接口是onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults),可以看到requestCode就是你刚才请求时传禁区的requestCode, grantResult数组就是用户操作的结果,类似代码如下:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PermissionUtils.MY_PERMISSION_REQUEST_CODE) {
boolean isAllGranted = true;
for (int grant : grantResults) {
//PERMISSION_GRANTED表示用户同意,PERMISSION_DENIED表示不同意
if (grant != PackageManager.PERMISSION_GRANTED) {
isAllGranted = false;
break;
}
}
// 判断是否所有的权限都已经授予了
if (isAllGranted) {
// 如果所有的权限都授予了, 则跳转录制界面
jumpToCameraPage();
} else {
// 弹出对话框告诉用户需要权限的原因, 并引导用户去应用权限管理中手动打开权限按钮
ToastUtils.showToast(this, "暂无权限");
}
}
}
但是由于Android各个厂商定制不一,所以假如我们要引导用户跳转到系统设置页,也需要兼容不同机型不同系统的目标Activity等,手动处理起来成本较大,目前网上有很多兼容性还不错的权限适配库,内部已经实现了一些机型的适配,可以很高效地方便我们完成权限适配工作,这里推荐一个比较热门的库——AndPermission。
Github地址:https://github.com/yanzhenjie/AndPermission
使用姿势:
1.首先gradle添加依赖
compile 'com.yanzhenjie:permission:2.0.0-rc4'
2.在需要申请的地方调用如下方法(这里以申请相机权限为例)
/**
* 申请相机权限
*/
public void requestPermission(){
AndPermission.with(this)
.runtime()
.permission(Manifest.permission.CAMERA)
.onGranted(new Action>() {
@Override
public void onAction(List data) {
// 用户同意授予权限
}
}).onDenied(new Action>() {
@Override
public void onAction(List permissions) {
// 用户拒绝授予权限
}
}).start();
}
runtime是表示这是申请运行时权限,permission传入你要申请的权限,onGranted是传入一个回调监听,监听用户操作的结果
效果图如下:
上面是基本的申请权限,假如用户同意了自然没啥问题,但万一用户拒绝了,我们的功能可能就无法正常使用,所以必须要给用户一个提示解释我们为何要申请这个权限,AndPermission库提供了自定义权限解释弹框的接口,先自定义Rationale如下:
/**
* 自定义权限解释弹框
*/
public final class RequestRationale implements Rationale> {
@Override
public void showRationale(Context context, List permissions, final RequestExecutor executor) {
//获得当前正在申请的危险权限的名字列表
//List permissionNames = Permission.transformText(context, permissions);
//遍历拼接
//String message = "";
//for(String permission : permissionNames){
//message = message + " " + permission;
//}
//Log.d(TAG, "当前申请的权限名列表: " + message);
AlertDialog.newBuilder(context)
.setCancelable(false)
.setTitle("权限申请")
.setMessage("我们需要使用相机来进行拍照和录制视频,否则将无法正常使用短视频功能")
.setPositiveButton("同意", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
executor.execute();
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
executor.cancel();
}
})
.show();
}
}
接着只需要在刚才申请权限那个方法处链式调用传入这个自定义Rationale:
/**
* 申请相机权限
*/
public void requestPermission(){
AndPermission.with(this)
.runtime()
.permission(Manifest.permission.CAMERA)
.rationale(new RequestRationale())
.onGranted(new Action>() {
@Override
public void onAction(List data) {
// 用户同意授予权限
}
}).onDenied(new Action>() {
@Override
public void onAction(List permissions) {
// 用户拒绝授予权限
}
}).start();
}
假如用户在一开始申请的时候拒绝了我们的权限,那会弹出二次解释弹框,效果如下:
用户一点击同意,就会再次触发申请权限的弹框,但此时弹框会多出一个“不再询问”的勾选框,假如用户勾选了并且再次点击了拒绝,那之后就不会触发这个rationale弹框了,那这种情况下该怎么处理呢?(总不可能就这样没有然后了吧= =、)
这就涉及到另外一个方法了,Android在设计时肯定有考虑到我们这种情况,提供了另外一个方法shouldShowRequestPermissionRationale(String permission)
从字面上理解就是:是否应该展示权限解释弹框,回想一下上一步,假如用户选择了拒绝并勾选了不再询问,那就代表用户永远不想授予权限,因此也就不需要再给用户解释,因此这个方法就返回false,反之假如用户只是拒绝一下但没有永远封杀你,那说明还有希望,这个方法依旧返回true,表示还可以继续解释。(但目前有些机子上例如小米由于被厂商修改,导致这个方法一直返回false)
(那这个方法对我们有什么帮助呢,假如用户拒绝了权限,那么AndPermission会一直回调onDenied中的Action回调,我们可以在这里面判断一下,用户是否勾选了不再询问,假如勾选了,那可以在这个回调中再弹出一个自定义弹框用来解释,类似如下:
/**
* 申请相机权限
*/
public void requestPermission(){
AndPermission.with(this)
.runtime()
.permission(Manifest.permission.CAMERA)
.rationale(new RequestRationale())
.onGranted(new Action>() {
@Override
public void onAction(List data) {
// 用户同意授予权限
}
}).onDenied(new Action>() {
@Override
public void onAction(List permissions) {
// 用户拒绝授予权限
checkAndShowDialog(permissions);
}
}).start();
}
public void checkAndShowDialog(List permissions){
if(AndPermission.hasAlwaysDeniedPermission(this, permissions)){
AlertDialog.newBuilder(this)
.setCancelable(false)
.setTitle("权限申请")
.setMessage("我们需要使用相机来进行拍照和录制视频,否则将无法正常使用短视频功能")
.setPositiveButton("同意", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.show();
}
}
当然,这种明明用户不再询问却还弹出提示的操作是体验不太好的,但是有时候一些权限对于你的应用来说是核心权限的情况下,只能采取这样的措施去引导用户打开权限开关(至于最终用户跳到系统权限管理页愿不愿意授权这个只能看造化了233)。
以上就是升级Android6.0必须要适配的运行时权限,虽然说权限没以前那么方便伸手即得,但毕竟也提高了用户和系统的安全性,对整个生态还是有益的。接下来我还会继续总结升级Android7.0以及8.0该适配的内容,未完待续~~