权限申请的用法
//acitivty中申请权限
ActivityCompat.requestPermissions(activity, permissions, requestCode);
//activity权限授权结果回调
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
源码解析
- ActivityCompat.requestPermissions()
- 对于6.0以下的手机,校验权限是否在Manifest.xml中声明过,并回调onRequestPermissionsResult()方法
- 对于6.0以上的手机,交给Activity.requestPermissions()处理
public static void requestPermissions(final @NonNull Activity activity,
final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode) {
if (sDelegate != null
&& sDelegate.requestPermissions(activity, permissions, requestCode)) {
// Delegate has handled the permission request.
return;
}
// 6.0以上手机
if (Build.VERSION.SDK_INT >= 23) {
// 是否实现了RequestPermissionsRequestCodeValidator接口,requestCode是否有效
if (activity instanceof RequestPermissionsRequestCodeValidator) {
((RequestPermissionsRequestCodeValidator) activity)
.validateRequestPermissionsRequestCode(requestCode);
}
// 调用Activity的申请权限方法
activity.requestPermissions(permissions, requestCode);
} else if (activity instanceof OnRequestPermissionsResultCallback) {
// 在主线程执行
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
final int[] grantResults = new int[permissions.length];
PackageManager packageManager = activity.getPackageManager();
String packageName = activity.getPackageName();
// 检查权限是否已经在Manifest.xml文件中声明过
final int permissionCount = permissions.length;
for (int i = 0; i < permissionCount; i++) {
grantResults[i] = packageManager.checkPermission(
permissions[i], packageName);
}
// 回调结果,流程结束
((OnRequestPermissionsResultCallback) activity).onRequestPermissionsResult(
requestCode, permissions, grantResults);
}
});
}
}
2.Activity.requestPermissions()
- 一次只能申请一组权限,在某一次权限申请没有回调结果之前,不允许再次申请权限
- 启动系统自带的权限管理的页面,将权限申请流程交给它处理。
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
if (requestCode < 0) {
throw new IllegalArgumentException("requestCode should be >= 0");
}
// mHasCurrentPermissionsRequest 防止重复申请的标记
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.
// 注意这里:重复的申请,会立即得到一个回调。这里的permissions和grantResults都是一个size为0的数组。
// 如果盲目的在Activity.onRequestPermissionsResult使用permissions[0]这样的处理,有可能产生数组越界。
onRequestPermissionsResult(requestCode, new String[0], new int[0]);
return;
}
// getPackageManager()获得PackageManager的实现类ApplicationPackageManager
// ApplicationPackageManager向PMS查询权限管理应用的包名,并使用系统给出的标准Action值。构建跳转系统权限管理页面的Intent对象
Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
mHasCurrentPermissionsRequest = true;
}
3.Activity.dispathActivityResult()
- 将权限请求的结果返回给当前Activity
void dispatchActivityResult(String who, int requestCode, int resultCode, Intent data,
String reason) {
if (false) Log.v(
TAG, "Dispatching result: who=" + who + ", reqCode=" + requestCode
+ ", resCode=" + resultCode + ", data=" + data);
mFragments.noteStateNotSaved();
if (who == null) {
// 眼熟吧
onActivityResult(requestCode, resultCode, data);
} else if (who.startsWith(REQUEST_PERMISSIONS_WHO_PREFIX)) {
who = who.substring(REQUEST_PERMISSIONS_WHO_PREFIX.length());
if (TextUtils.isEmpty(who)) {
// 回调权限处理结果
dispatchRequestPermissionsResult(requestCode, data);
} else {
Fragment frag = mFragments.findFragmentByWho(who);
if (frag != null) {
dispatchRequestPermissionsResultToFragment(requestCode, data, frag);
}
}
}
}
4.Activity.dispatchRequestPermissionsResult()
private void dispatchRequestPermissionsResult(int requestCode, Intent data) {
mHasCurrentPermissionsRequest = false;
// If the package installer crashed we may have not data - best effort.
String[] permissions = (data != null) ? data.getStringArrayExtra(
PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES) : new String[0];
final int[] grantResults = (data != null) ? data.getIntArrayExtra(
PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS) : new int[0];
// 回调结果,流程结束
onRequestPermissionsResult(requestCode, permissions, grantResults);
}
注意事项
一次只能申请一组权限
如果发起重复的权限请求,会造成onRequestPermissionsResult()立即回调。这里的参数都跟预想中不一致,回调处处理适当的话,可能会产生数组越界问题,甚至栈溢出等不可预知的情况
不要在onResume()中申请权限
申请权限的过程,实质上是一个Activity跳转的过程。那么必然涉及到Activity生命周期的转换。当前Activity会有onResume() -> onPause() -> onResume()这样一个流程。如果在onResume()中申请权限,那么当结果回调回来的时候会再次发起权限的申请,最终陷入一个死循环中。
startActivityForResult()是一个跨进程通信的过程,在一次请求中,Activity.requestPermissions()方法在执行完startActivityForResult()之后就已经结束了。相关方法的栈帧将会被回收,并不会产生栈溢出的问题,而是造成大量的内存占用,比较难以排查。