EasyPermissions轻松搞定动态权限申请

android 6.0的发布对权限系统进行了改变,部分“Dangerous Permission”需要在运行时询问申请,这中间涉及到与用户交互问题,例如 用户操作 同意 拒绝 不再询问 都要一一做出响应,很是繁琐, 目前google已经推出了开源项目Easypermissions来简化这一操作

相关文档

GitHub地址
easypermissions

Google官网介绍
在运行时请求权限

使用步骤

添加依赖

1
2
3
4
5
6
7
dependencies {
// For developers using AndroidX in their applications
implementation 'pub.devrel:easypermissions:3.0.0'

// For developers using the Android Support Library
implementation 'pub.devrel:easypermissions:2.0.1'
}

检查运行时读写权限

1
EasyPermissions.hasPermissions(this, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE)

这一步很好理解检查我们需要的权限是否已经授权,废话不多说直接上源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* Check if the calling context has a set of permissions.
*
* @param context the calling context.
* @param perms one ore more permissions, such as {@link Manifest.permission#CAMERA}.
* @return true if all permissions are already granted, false if at least one permission is not
* yet granted.
* @see Manifest.permission
*/
public static boolean hasPermissions(@NonNull Context context,
@Size(min = 1) @NonNull String... perms) {
// Always return true for SDK < M, let the system deal with the permissions
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
Log.w(TAG, "hasPermissions: API version < M, returning true by default");

// DANGER ZONE!!! Changing this will break the library.
return true;
}

// Null context may be passed if we have detected Low API (less than M) so getting
// to this point with a null context should not be possible.
if (context == null) {
throw new IllegalArgumentException("Can't check permissions for null context");
}

for (String perm : perms) {
if (ContextCompat.checkSelfPermission(context, perm)
!= PackageManager.PERMISSION_GRANTED) {

return false;
}
}

return true;
}

首先我们来看看参数 context 上下文为空直接抛出IllegalArgumentException异常需要注意
从Java 5开始,Java语言对方法参数支持一种新写法,叫可变长度参数列表就是我们的第二个参数String… 类似String[]这种形式非常实用, 延展性更强, 返回boolean值不用多想ture就是权限已经拿到false就是要申请权限了

申请权限

1
2
3
4
5
6

EasyPermissions.requestPermissions(
this,
"需要访问读写权限",
RC_READ_PERM,
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE);

分析下这步操作需要的四个参数

  1. activity 用它来构建一个PermissionHelper,这个是干嘛的等后面看源码的时候细细分析
  2. 这个是请求弹窗中给用户的提示语
  3. 这个请求code 用来追踪权限请求,后面我们收到权限会用它来判断
  4. 可变长度参数可申请多个权限(一组权限)

源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Request a set of permissions, showing a rationale if the system requests it.
*
* @param host requesting context.
* @param rationale a message explaining why the application needs this set of permissions;
* will be displayed if the user rejects the request the first time.
* @param requestCode request code to track this request, must be &lt; 256.
* @param perms a set of permissions to be requested.
* @see Manifest.permission
*/
public static void requestPermissions(
@NonNull Activity host, @NonNull String rationale,
int requestCode, @Size(min = 1) @NonNull String... perms) {
requestPermissions(
new PermissionRequest.Builder(host, requestCode, perms)
.setRationale(rationale)
.build());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package etcp.payment.machine.easypermissions;


/**
* Construct a new permission request builder with a host, request code, and the requested
* permissions.
*
* @param activity the permission request host
* @param requestCode request code to track this request; must be &lt; 256
* @param perms the set of permissions to be requested
*/
public Builder(@NonNull Activity activity, int requestCode,
@NonNull @Size(min = 1) String... perms) {
mHelper = PermissionHelper.newInstance(activity);
mRequestCode = requestCode;
mPerms = perms;
}

接下来我们分析下 PermissionHelper

1
private final PermissionHelper mHelper;

这是一个抽象类下面四个抽象函数 分别是:请求权限/检查权限是否被永久拒绝/弹出权限交互对话框/获取上下文,

1
2
3
4
5
6
7
8
9
10
11
12
13
//请求权限
public abstract void directRequestPermissions(int requestCode, @NonNull String... perms);
//检查权限是否被永久拒绝
public abstract boolean shouldShowRequestPermissionRationale(@NonNull String perm);
//弹出权限交互对话框
public abstract void showRequestPermissionRationale(@NonNull String rationale,
@NonNull String positiveButton,
@NonNull String negativeButton,
@StyleRes int theme,
int requestCode,
@NonNull String... perms);
获取上下文
public abstract Context getContext();

有了这个抽象类我们就明白Easypermissons内部是怎么工作的了。

请求权限结果回调

easypermissions的权限回调需要我们把系统的回调结果交给它好让它为我们做简化处理,我们只需在onRequestPermissionsResult的回调中加入一行,如下

1
2
3
4
5
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}

前三个参数对应传递,最后一个是接口用来把处理结果回调给我们

1
2
3
4
5
6
7
8
9
10
11
/**
* Callback interface to receive the results of {@code EasyPermissions.requestPermissions()}
* calls.
*/
public interface PermissionCallbacks extends ActivityCompat.OnRequestPermissionsResultCallback {

void onPermissionsGranted(int requestCode, @NonNull List<String> perms);

void onPermissionsDenied(int requestCode, @NonNull List<String> perms);

}
  1. onPermissionsGranted权限被同意。
  2. requestpermissions() 权限被拒绝

如果要知道这个拒绝是否被用户勾选为不再询问的话,这就的需要下面这个函数来判断了

1
EasyPermissions.somePermissionPermanentlyDenied(this, perms));

这个函数其实就是调用的我们前面讲到的的抽象类PermissionHelper中的抽象函数shouldShowRequestPermissionRationale
源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Check if at least one permission in the list of denied permissions has been permanently
* denied (user clicked "Never ask again").
*
* @param host context requesting permissions.
* @param deniedPermissions list of denied permissions, usually from {@link
* PermissionCallbacks#onPermissionsDenied(int, List)}
* @return {@code true} if at least one permission in the list was permanently denied.
*/
public static boolean somePermissionPermanentlyDenied(@NonNull Activity host,
@NonNull List<String> deniedPermissions) {
return PermissionHelper.newInstance(host)
.somePermissionPermanentlyDenied(deniedPermissions);
}

案例

下面是我自己写的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

public class StartActivity extends Activity implements EasyPermissions.PermissionCallbacks{

private final int RC_READ_PERM = 1;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
execPermission();
}


protected void execPermission() {

if (EasyPermissions.hasPermissions(this, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
// 已经获取到权限
} else {
// 未获取到权限 执行权限请求
EasyPermissions.requestPermissions(
this,
"需要访问读写权限",
RC_READ_PERM,
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE);
}

}


@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}

@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
if (requestCode == RC_READ_PERM && EasyPermissions.hasPermissions(this, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
startActivity(WebScriptActivity.getIntent(StartActivity.this));
finish();
}
}

@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) { // 权限被勾选为不再询问
Toast.makeText(this, "需要访问读写权限", Toast.LENGTH_SHORT).show();
}
}
}