在sdk23以前,用户安装应用时必须同意开发者添加的所有权限,否则应用无法安装成功。对于开发者来说,只需要在清单文件里添加功能模块需要的权限,非常方便。对于用户来说,却是灾难性的,数据隐私完全暴露在应用面前。
于是,Google的Android团队在6.0版本推出了运行时权限系统,部分有关用户隐私的权限被划分为危险权限。对于开发者来说,普通权限的添加与以前并无两样,但危险权限不仅需要在清单文件中配置,还需要添加Java代码弹出对话框向用户请求权限。由于系统对动态权限已封装完善,咱们只需要调用新的API向用户请求权限即可,对话框也是系统提供,无需也不能自定义UI。
本篇文章将会先介绍动态权限相关的四个方法,然后介绍如何使用这四个方法向用户申请动态权限。弹出权限请求的对话框后,用户可以同意权限,拒绝权限,拒绝权限并不再提示,APP要根据自己的业务需求,去处理用户的三种界面交互。在诸多应用中,腾讯公司的“微信”是一款用户体验较好的产品。因此,本篇文章会以微信的“扫一扫”功能需要的相机权限为例,模仿其界面交互的逻辑。
关于危险权限的介绍,详细见官方文档。危险权限中有10个权限组,权限组在类Manifest的内部类permission_group中定义,详情见官方文档。每个权限组中包含几个权限,权限在类Manifest的内部类permission中定义,详情见官方文档;
危险权限,如下所示:
检查应用是否拥有某一权限permission
ContextCompat类中:
public static int checkSelfPermission(@NonNull Context context,
@NonNull String permission)
permission:权限对应的字符串,在Manifest.permission中定义
eg: 相机权限,第二个参数则传入Manifest.permission.CAMERA
返回值:int类型,有两种情况
PackageManager.PERMISSION_GRANTED:表示应用有该权限
PackageManager.PERMISSION_DENIED: 表示应用没有该权限
-------------------------------------------------------
向用户请求权限,此时系统会弹出对话框
ActivityCompat类中:
public static void requestPermissions(final @NonNull Activity activity,
final @NonNull String[] permissions,
final @IntRange(from = 0) int requestCode)
permissions:字符串数组,批量向用户请求权限
requestCode:标记这次权限请求,常量可自定义
-------------------------------------------------------
用户与对话框交互后,处理权限请求的结果
一般需要重写该方法
Activity类中
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions, @NonNull int[] grantResults) {
requestCode: 对应requestPermissions方法中的requestCode
permissions: 对应requestPermissions方法中的permissions
grantResults:权限请求的结果
权限请求的结果有两种值:
PackageManager.PERMISSION_GRANTED:表示用户同意请求
PackageManager.PERMISSION_DENIED: 表示用户拒绝请求
--------------------------------------------------------
用户是否选择对话框中“禁止后不再询问”
ActivityCompat类中
public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity,
@NonNull String permission)
参数比较简单,不再介绍
返回值:
true: 没有勾选“禁止后不再询问”,下次请求权限,仍弹出对话框
false: 已经勾选“禁止后不再询问”,下次请求权限,不会弹出对话框
定义一些权限,并过滤掉应用已获取的权限,代码如下:
private String[] permissions = {Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE};
private ArrayList needToPermissions = new ArrayList();
private void getNotAgreePermission() {
for (String p : permissions) {
if (ContextCompat.checkSelfPermission(this, p) != PackageManager.PERMISSION_GRANTED) {
//如果权限没有被授予
needToPermissions.add(p);
}
}
}
提供一个requestPermissions方法向用户发起权限请求,代码如下:
private static final int REQUEST_CODE_PERMMISSIONS = 1;
private void requestPermissions() {
//过滤掉已拥有的权限
getNotAgreePermission();
//如果已获得所有权限
if (needToPermissions.size() <= 0)
return;
ActivityCompat.requestPermissions(
this,
//集合转化为数组
needToPermissions.toArray(new String[needToPermissions.size()]),
//int值标识这次权限请求
REQUEST_CODE_PERMMISSIONS);
}
此时,用户界面会弹出系统权限请求的对话框。
我们需要先在“设置-应用-微信-权限”中将微信的相机权限关闭,打开微信的扫一扫,研究对话框的交互逻辑。在微信的扫一扫中,只要用户点击“禁止”,不管是否勾选“禁止后不再询问”,都会弹出一个对话框提示用户在设置里手动开启权限。
以微信的扫一扫权限请求的逻辑为例,继续处理与用户交互的结果(实际情况以具体业务需求为准);
系统回调onRequestPermissionsResult方法,代码如下:
//处理权限请求的对话框逻辑:同意/拒绝
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
//requestCode :请求的唯一标识
//permissions: 请求的权限
//grantResults:请求的结果
//打印参数中的数据
for (int i = 0; i < grantResults.length; i++) {
Log.e("wcc", "requestCode : " + requestCode);
Log.e("wcc", "permissions : " + permissions[i]);
Log.e("wcc", "result : " + grantResults[i]);
}
//仅以申请相机权限为例
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//用户同意授权
//do something...
Log.e("wcc", "request success");
} else {
//用户不同意授权
if (ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[0])) {
//仅仅拒绝
//以微信的扫一扫为例:弹出对话框
Log.e("wcc", "request failed and still remind next time");
showDialog();
} else {
//不仅拒绝,还不再提醒
//以微信的扫一扫为例:弹出对话框
Log.e("wcc", "request failed and never remind");
showDialog();
}
}
}
private AlertDialog dialog;
//模仿微信交互方式,禁止后弹出对话框
private void showDialog() {
dialog = new AlertDialog.Builder(this)
.setTitle("权限申请")
.setMessage("在设置-应用-微信-权限中开启相机权限,以正常使用拍照,小视频,扫一扫等功能")
.setCancelable(false)
.setPositiveButton("去设置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getApplicationContext(), "start Settings", Toast.LENGTH_SHORT).show();
//startActivity ... and enter Setting
finish();
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getApplicationContext(), "cancel", Toast.LENGTH_SHORT).show();
finish();
}
})
.show();
}
在Activity的onCreate方法中,调用requestPermissions方法向用户请求权限;
代码如下:
public class MainActivity extends Activity {
private TextView text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = findViewById(R.id.text);
requestPermissions();
}
//... code
@Override
protected void onDestroy() {
if (dialog != null) {
dialog.cancel();
}
super.onDestroy();
}
}
每次onCreate方法被调用,都会尝试向用户请求权限。
在此之前,如果用户点击“禁止”按钮,并勾选了“禁止后不再询问”。那么,在调用ActivityCompat$requestPermissions向用户请求时,不会弹出对话框,且仍会回调onRequestPermissionsResult方法,grantResults参数的值是PERMISSION_DENIED,默认用户禁止获取该权限。
最后,本篇文章到此结束啦~
O(∩_∩)O