Android M之后,有的在manifest定位的权限需要在运行时动态申请才可以使用,这方面的详细信息可以参考官方文档:https://developer.android.com/training/permissions/requesting?hl=zh-cn,一般是需要申请权限时,需要检查是否已经有权限,如果没有则申请,主要使用到如下步骤:
首先使用ContextCompat.checkSelfPermission检查,第一参数是context,第二个是要检查的权限名称,然后用ActivityCompat.requestPermissions去申请权限,第一个参数是context,第二个是要申请的权限,第三个是这次申请的requestCode
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}
在调用ActivityCompat.requestPermissions申请后,系统会弹出一个提示框提示用户是否允许,当用户点了允许或拒绝时会回调到activity的onRequestPermissionsResult函数:
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
第一个参数是申请时的requestCode,第二个是申请的权限,第三个是申请权限的结果.
以上是官方的教程,如果按这个教程去做的话,你需要在每个activity都写一遍onRequestPermissionsResult回调的处理,这对于代码的维护来说是非常不好的,也干扰了正常的业务逻辑代码。那提取一个权限申请库就非常有必要了,要实现申请功能并不难,难点在于,怎么让申请库的代码不跟原有的activity代码耦合在一起呢,下面就介绍一种解耦的方法:
方法一:
自己建一个用来权限申请没有界面的activity,在申请是启动这个activity去申请,然后在这个activty里的回调来接收结果,这种方法可以做到解耦,但是需要在manifest里定义这个activity,AndPermission就是这样实现的,这样个人感觉还不够极致,那进一步的方法是方法
方法二:
这种方法是不使用activity了,而是使用Fragment,因为Fragment也有activity一样的申请权限函数和回调,这种就不用在manifest中定义什么了,RxPermission就是这样实现的
这里不去具体讲实现的代码了,这里是想分享一种跟生命周期有关的逻辑怎么跟原有代码逻辑解耦方法,上面两种其实都是可以达到目的的方法,这种方法在一些开源库中其实都有,在Glide中,就有使用Fragment在onDestroy时做资源释放的,代码:
// RequestmanagerRetriever.java
private RequestManager fragmentGet(@NonNull Context context,
@NonNull android.app.FragmentManager fm,
@Nullable android.app.Fragment parentHint,
boolean isParentVisible) {
RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible);
RequestManager requestManager = current.getRequestManager();
if (requestManager == null) {
// TODO(b/27524013): Factor out this Glide.get() call.
Glide glide = Glide.get(context);
requestManager =
factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
current.setRequestManager(requestManager);
}
return requestManager;
}
// RequestManagerFragment.java
@NonNull
ActivityFragmentLifecycle getGlideLifecycle() {
return lifecycle;
}
@Override
public void onStop() {
super.onStop();
lifecycle.onStop();
}
// RequestManagerFragment.java
@Override
public void onDestroy() {
super.onDestroy();
lifecycle.onDestroy();
unregisterFragmentWithRoot();
}
Glide在Fragment的onDestory会调用Glide的lifecycle的onDestroy,然后做资源的释放,这个使用的过程不会有任何干扰,开发者只管放心地使用,那用这种方式使用的权限申请库也是一样,开发者不用关心这些细节,只要简单的调用接口就好,下面实现的申请库使用示例:
PermissionChecker.with(this)
.permission(Manifest.permission.WRITE_EXTERNAL_STORAGE, "存储权限不可用", "XXX需要存储权限以正常工作,是否允许?")
.permission(Manifest.permission.ACCESS_COARSE_LOCATION, "定位权限不可用", "XXX需要定位权限以正常工作,是否允许?")
.onAllow(new PermissionChecker.AllowCallback() {
@Override
public void onAllow(String permission) {
Toast.makeText(MainActivity.this, "allow", Toast.LENGTH_SHORT).show();
}
}).onDeny(new PermissionChecker.DenyCallback() {
@Override
public void onDeny(String permission) {
Toast.makeText(MainActivity.this, "deny", Toast.LENGTH_SHORT).show();
}
}).build().check();
代码在github:https://github.com/Cmdmac/PermissionChecker