0%

(09)2021.12.28-PKMS权限申请实战---derry老师

6.0 以后,Android 权限分为正常权限和危险权限,危险权限有以下几种:

  • calendar
  • Camera
  • Location
  • Phone-拨打电话
  • SMS -短信相关
  • Storage -读取存储相关的权限

权限是一组一组的,如果你申请了 读取 存储的权限,那么写存储的权限可以不用,因为它们是同一组的。

权限申请整体代码过程如下图所示:

权限申请代码流程

在 Activity 的 requestPermissions 的方法里面,最终会调用 startActivityForResult 启动授权 Activity ,这也是为什么我们能收到 onRequestPermissionsResult 的原因:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
if (requestCode < 0) {
throw new IllegalArgumentException("requestCode should be >= 0");
}
if (mHasCurrentPermissionsRequest) {
Log.w(TAG, "Can request only one set of permissions at a time");
// Dispatch the callback with empty arrays which means a cancellation.
onRequestPermissionsResult(requestCode, new String[0], new int[0]);
return;
}
Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
mHasCurrentPermissionsRequest = true;
}

这里面有个小的知识点,Intent 是通过 build 创建出来的,这里是启动隐式的 App:

1
2
3
4
5
6
7
8
9
public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) {
if (ArrayUtils.isEmpty(permissions)) {
throw new IllegalArgumentException("permission cannot be null or empty");
}
Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);
intent.setPackage(getPermissionControllerPackageName());
return intent;
}

直接使用 new Intent(ACTION_REQUEST_PERMISSIONS) 的方式,这是通过隐式意图来激活某个 Activity ,根据常量 ACTION_REQUEST_PERMISSIONS 可以在源码找到(肯定是在 AndroidManifest.xml 中)这个 Activity 就是 GrantPermissionsActivity 。这种隐藏的 App (比如apk安装器)我们直接启动是启动不了的。

用户点击确认授权,会通过进程间通信交给PermissionManagerService 去处理,当然,过程还需要 PKMS 去查询这个 App 。

当授权通过后,会通过 Settings.writeRuntimePermissionxxx 方法将这个记录保留到 xml (system/users/0/runtime-permissions.xml 文件)中永久保存,此后会一直存在,如果卸载 App ,就将这个记录删除掉。

权限申请源码流程总结:

第一步:MainActivity 调用 requestPermissions 进行动态权限申请;
第二步:requestPermissions函数通过隐式意图,激活PackageInstaller的GrantPermissionsActivity界面,让用户选择是否授权;
第三步:经过PKMS把相关信息传递给PermissionManagerService处理;
第四步:PermissionManagerService处理结束后回调给—->PKMS中的onPermissionGranted方法把处理结果返回;
第五步:PKMS通知过程中权限变化,并调用writeRuntimePermissionsForUserLPr函数让PackageManager的settings记录下相关授权信息;

手写无侵入式框架

为什么要写?我短短一个 Activity 代码,申请权限却要写很长很长的权限申请代码。

权限申请

权限被取消

权限被取消,还勾选不再提示

可以用到 AspectJ ,这个后台用到,可以控制注解方法的执行不执行,满足条件执行 A 方法,不满足不执行。这个框架很复杂,是 javac 的二次封装。

这个框架的设计图如下:

无侵入框架设计图

以后的框架,应该都需要是 ,无侵入式的(由框架劫持用户的行为) AspectJ 劫持函数的执行

AspectJ 为什么可以无侵入式的 监听+劫持 我们的任何注解
javac Test.java Test.class (JVM只认识class) (我们看不懂字节码,JVM看得懂)
AspectJ(Javac) Test.java 注入代码 Test.class (JVM只认识class) (我们看不懂字节码,JVM看得懂)

空白的Activity(申请权限 申请成功 申请失败 用户拒绝申请 回调给外界 告诉AspectJ)

任何一个框架,都有三种方式实现:
1.无侵入式的 由框架监听用户 劫持用户的行为,用户是没有能力调用框架的(依赖AspectJ)
2.APT 注解处理器 侵入式的框架,编译期 Dagger2 Room ARouter DataBinding
3.传统 xUtils 反射

无侵入式:用户没有能力调用我们的框架,它连看都看不到我们的框架,
是由我们的框架,全局监听用户的行为,劫持用户,控制用户,执行用户

我们框架特点:
我只需要使用三个注解就行了

用户没有能力调用框架的API

用户也不需要传递this

由我们的框架 来 监听用户的 注解的

面试题

动态申请权限的流程?

有 15 步,应用进程跨系统服务进程 PKMS 通过 PermissionManagerService 检查权限情况,回调回来给 PKMS ,并且通过 Settings 通过 io 操作将权限结果写入 xml 文件中永久保存,除非卸载,才删除这个记录

谢谢你的鼓励