Permissions on Android

Those pitfalls I fall into…

Conditon

  1. 危险权限
  2. Android 版本 >= 6.0
  3. targetSdkVersion >= 23
    如果项目的targetSdkVersion < 23, 在Android 6.0+的手机上,会默认给予所有在AndroidManifest.xml中申请的权限。

是不是觉得这样就完事大吉了?

如果用户在应用的权限页面手动收回权限,将会导致应用Crash.💔

稳妥的处理当然是遵循Google的权限申请机制。

API

为方便开发者实现权限管理,Google提供了4个API:

  1. checkSelfPermission( ) 判断权限是否具有某项权限
  2. requestPermissions( ) 申请权限
  3. onRequestPermissionsResult( ) 申请权限回调方法
  4. shouldShowRequestPermissionRationale( ) 是否要提示用户申请该权限的缘由

Pitfall

如果用户任性的勾选了“不再询问”,那么在执行requestPermissions( )后,onRequestPermissionsResult( )会永远返回PERMISSION_DENIED,这样应用原本的操作将永远无法执行。

Right way

shouldShowRequestPermissionRationale( )

如果返回为true,一般情况下,应用应该弹出Dialog说明申请该权限的缘由

shouldShowRequestPermissionRationale( )只会在一种情况下返回true

用户上一次拒绝申请权限,但是并未勾选“不再询问”

如何判断用户勾选了“不再询问”?

从上面的表格可以看出,如果上次shouldShowRequestPermissionRationale( )返回了true,而这次调用该方法返回了false,则说明用户在上次勾选了“不再询问”。此时,我们需要引导用户进入设置界面进行权限授予。

由于涉及到上一次调用shouldShowRequestPermissionRationale( )的结果,所以需要将其持久化保存,SharedPreferences或者数据库均可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void requestPermission(Activity activity, final String permission) {
boolean flag = ActivityCompat.shouldShowRequestPermissionRationale(activity, permission);
if (getLastRequestState() && !flag) {
//当用户勾选`不再询问`时, 进入设置界面
Uri uri = Uri.fromParts("package", this.getPackageName(), null);
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, uri);
startActivityForResult(intent, COME_CODE);
} else if (flag) {
//之前有过`拒绝`授权时,提醒用户需要某权限
showRationaleDialog();
//同时保存返回值
SharedPrefsUtils.setBooleanPreference(getApplicationContext(), KEY_RESUEST_SOME_PERMISSION, flag);
} else {
//第一次申请权限时,直接申请权限
ActivityCompat.requestPermissions(activity, new String[]{permission}, REQUEST_PERMISSION_CODE);
}
}

流程图

Best Practice

Process Step

上面的解决方案是可行的,但是每次申请权限需要依赖于上一次调用shouldShowRequestPermissionRationale( )方法的返回值,如果SharedPreferences被修改或者被删除,会影响正常的申请流程。

Google提供了一个非常好的思路,详见EasyPermissions .

EasyPermissions并没有存储上一次shouldShowRequestPermissionRationale( )的返回值,而是在申请权限被拒后调用shouldShowRequestPermissionRationale( )方法,如果此时返回false则说明用户勾选了“不再询问”。

简化判断“不再询问”的条件

1.未获得授权
2.shouldShowRequestPermissionRationale( )返回false

Reference

[]Android M 权限最佳实践
[2]android permission权限与安全机制解析(上)
[3]android permission权限与安全机制解析(下)
[4]第三方库PermissionsDispatcher
[5]EasyPermissions
[6]Android权限最佳实践
[7]Android如何获取判断是否有悬浮窗权限
Android悬浮窗实现 使用WindowManager
突破小米悬浮窗权限控制 – 不需要权限的悬浮窗
Android无需权限显示悬浮窗, 兼谈逆向分析app