背景
动态申请权限是一件特别繁琐的事情,涉及的权限一多,就让人头疼。Android提供给我们的运行时权限申请的API并不是很友好。
比如我们需要动态申请打电话的功能。除了在roidManifest.xml中声明权限之外,还要在执行拨打电话操作之前进行动态申请才行。
<uses-permission android:name="android.permission.CALL_PHONE"/>
然后在代码中动态申请:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, 1);
} else {
call();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
call();
} else {
Log.d("TAG", "onRequestPermissionsResult: -------------");
}
}
}
这个就是我们动态申请一般的做法
先判断用户是否已经授予了CALL_PHONE权限,授予了就可以直接执行call方法,如果没有就需要去动态申请,然后咋onRequestPermissionsResult()回调中处理权限申请的结果,最后才能去执行拨打电话的操作。
可能觉得不是很繁琐,那是因为一开始我们学习就是这样做的,并不会觉得有什么“排斥”。
但是如果权限一多不止一个,而是需要同时申请多个权限,虽然ActivityCompat.requestPermissions()方法允许一次性传入多个权限名,但是你在onRequestPermissionsResult()回调中就需要判断哪些权限被允许了,哪些权限被拒绝了,被拒绝的权限是否影响到应用程序的核心功能,以及是否要再次申请权限。
而一旦牵扯到再次申请权限,就引出了一个更加复杂的问题。你申请的权限被用户拒绝过了一次,那么再次申请将很有可能再次被拒绝。为此,Android提供了一个shouldShowRequestPermissionRationale()方法,用于判断是否需要向用户解释申请这个权限的原因,一旦shouldShowRequestPermissionRationale()方法返回true,那么我们最好弹出一个对话框来向用户阐明为什么我们是需要这个权限的,这样可以增加用户同意授权的几率。
而且Android系统还提供了一个“拒绝,不要再询问”的选项。
只要用户选择了这个选项,那么我们以后每次执行权限申请的代码都将会直接被拒绝。
可是如果我的某项功能就是必须要依赖这个权限才行呢?没有办法,你只能提示用户去应用程序设置当中手动打开权限,程序方面已无法进行操作。
可以看出,如果想要在项目中对运行时权限做出非常全面的处理,是一件相当复杂的事情。
所以PermissionX就此诞生
先给出开源库的**原地址**
运用
最开始PermissionX是只支持kt的,但是作者(郭霖)结合国内开发人员使用java开发Android更多,进而开发了java版本的PmissionX。
比如前面的例子,现在就可以写成:
implementation 'com.permissionx.guolindev:permission-support:1.4.0'
先在项目中添加依赖(目前的最新版本):
PermissionX.init(this)
.permissions(Manifest.permission.CALL_PHONE)
.request(new RequestCallback() {
@Override
public void onResult(boolean allGranted, List<String> grantedList, List<String> deniedList) {
if (allGranted) {
call()
} else {
Log.d("TAG", "onResult: ---");
}
}
});
是不是就很简单~
核心用法:
PermissionX.init(this)
//需要申请的权限list
.permissions(permissionList)
//用于申请权限开始之前向用户解释原因
.explainReasonBeforeRequest()
//设置dialog PositiveButton和NegativeButton字体颜色
.setDialogTintColor(Color.parseColor("#008577"), Color.parseColor("#83e8dd"))
//点击禁止弹出的dialog,点击“我已明白”,
// 会再次弹出申请权限的弹窗,
// 点击取消在下次进入应用的时候会再次弹出申请权限的弹窗
.onExplainRequestReason(new ExplainReasonCallbackWithBeforeParam() {
@Override
public void onExplainReason(ExplainScope scope, List<String> deniedList, boolean beforeRequest) {
scope.showRequestReasonDialog(deniedList, "即将申请的权限是程序必须依赖的权限", "我已明白","取消");
}
})
//点击了禁止后不再询问弹出的dialog,
// 点击“我已明白”,就会到设置界面引导用户打开权限,如果未打开所有权限,则这个dialog不会消失,
// 点击取消,下次进入会再次弹出这个dialog。
.onForwardToSettings(new ForwardToSettingsCallback() {
@Override
public void onForwardToSettings(ForwardScope scope, List<String> deniedList) {
scope.showForwardToSettingsDialog(deniedList, "您需要去应用程序设置当中手动开启权限", "我已明白","取消");
}
})
.request(new RequestCallback() {
@Override
public void onResult(boolean allGranted, List<String> grantedList, List<String> deniedList) {
if (allGranted) {
Toast.makeText(MainActivity.this, "所有申请的权限都已通过", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "您拒绝了如下权限:" + deniedList, Toast.LENGTH_SHORT).show();
}
}
});
代码中的注释是根据我个人的理解写的
当然AndroidManifest.xml中也必须要声明这些权限。
自定义权限提醒对话框
如果你觉得自带的权限提醒对话框太丑了,那么其他人也这样觉得,所以PermissionX还支持自定义权限提醒对话框。
一个简单的例子:
.onExplainRequestReason(new ExplainReasonCallbackWithBeforeParam() {
@Override
public void onExplainReason(ExplainScope scope, List<String> deniedList, boolean beforeRequest) {
// scope.showRequestReasonDialog(deniedList, "即将申请的权限是程序必须依赖的权限", "我已明白","取消");
MyDialog myDialog = new MyDialog(context,R.style.Theme_AppCompat_DayNight_Dialog,deniedList);
scope.showRequestReasonDialog(myDialog);
}
})
public class MyDialog extends RationaleDialog {
private List<String> list;
private Context context;
private int themeResId;
private Button ok,qx;
private TextView title,message;
public MyDialog(@NonNull Context context, int themeResId, List<String> list) {
super(context, themeResId);
this.context = context;
this.themeResId = themeResId;
this.list = list;
setContentView(R.layout.dialog_alter);
getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); //圆角起作用
ok = findViewById(R.id.tv_ok);
qx = findViewById(R.id.tv_qx);
title = findViewById(R.id.tv_title);
message = findViewById(R.id.tv_message);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@NonNull
@Override
public View getPositiveButton() {
Log.d("TAG", "getPositiveButton: ");
return ok;
}
@Nullable
@Override
public View getNegativeButton() {
Log.d("TAG", "getNegativeButton: ");
return qx;
}
@NonNull
@Override
public List<String> getPermissionsToRequest() {
return list;
}
}
需要继承RationaleDialog,而RationaleDialog又是继承Dialog的。
RationaleDialog源码:
public abstract class RationaleDialog extends Dialog {
public RationaleDialog(@NonNull Context context) {
super(context);
}
public RationaleDialog(@NonNull Context context, int themeResId) {
super(context, themeResId);
}
protected RationaleDialog(@NonNull Context context, boolean cancelable, @Nullable OnCancelListener cancelListener) {
super(context, cancelable, cancelListener);
}
/**
* Return the instance of positive button on the dialog. Your dialog must have a positive button to proceed request.
* @return The instance of positive button on the dialog.
*/
abstract public @NonNull View getPositiveButton();
/**
* Return the instance of negative button on the dialog.
* If the permissions that you request are mandatory, your dialog can have no negative button.
* In this case, you can simply return null.
* @return The instance of positive button on the dialog, or null if your dialog has no negative button.
*/
abstract public @Nullable View getNegativeButton();
/**
* Provide permissions to request. These permissions should be the ones that shows on your rationale dialog.
* @return Permissions list to request.
*/
abstract public @NonNull List<String> getPermissionsToRequest();
}
后面三个方法都是必须实现。
settitle,setmessage这些都没有实现,这里试试简单试了功能下可以自定义提醒框
还有一些其他的功能
支持Android11
支持Fragment
还有写其他功能可以查看作者的CSDN