Android 权限申请源码解析

本文详细解析了Android权限申请的用法,包括ActivityCompat.requestPermissions()和Activity.requestPermissions()的处理流程。强调了一次只能申请一组权限的重要性,避免在onResume()中申请权限以防止死循环的发生。同时指出,重复请求权限可能导致onRequestPermissionsResult()回调异常,增加内存占用,应谨慎处理。

权限申请的用法

//acitivty中申请权限
ActivityCompat.requestPermissions(activity, permissions, requestCode);

//activity权限授权结果回调
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

源码解析

  1. 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()之后就已经结束了。相关方法的栈帧将会被回收,并不会产生栈溢出的问题,而是造成大量的内存占用,比较难以排查。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值