Android之EasyPermissions源码解析

转载请标明出处:【顾林海的博客】

个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持!
在这里插入图片描述


前言

我们知道在Android中想要申请权限就需要在AndroidManifest配置文件中通过uses-permission标签设置申请的权限,通过这种方式申请权限固然方便,但在安全性方面却不高,比如开发者申请获取用户隐私的权限,这样用户在不知情的情况下获取到了用户的隐私,如何避免这种不安全的权限获取?从Android 6.0开始,Google将权限分为两类,分别是默认权限(Normal Permission)和危险权限(Dangerous Permission),默认权限在程序安装时授权,但危险权限需要用户在运行APP时手动授权,这里使用EasyPermissions库来简化运行时权限的操作。

EasyPermissions使用

/**
 * 权限管理类
 *
 * @author glh
 */
public abstract class BasePermissionsActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestPermissions(this);
    }

    public void requestPermissions(Context context) {
        String[] perms = {Manifest.permission.CAMERA,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.READ_PHONE_STATE};
        if (!EasyPermissions.hasPermissions(context, perms)) {
            EasyPermissions.requestPermissions((Activity) context, context.getResources().getString(R.string.camera_rationale),
                    124, perms);
        }
    }

    @Override
    public void onPermissionsGranted(int requestCode, List<String> perms) {
        ToastUtils.showToast(this, "用户授权成功");
    }

    @Override
    public void onPermissionsDenied(int requestCode, List<String> perms) {
        /*
         * 若是在权限弹窗中,用户勾选了'NEVER ASK AGAIN.'或者'不在提示',且拒绝权限。
         * 这时候,需要跳转到设置界面去,让用户手动开启。
         */
        if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
            new AppSettingsDialog
                    .Builder(this)
                    .setTitle("\"某APP\"权限提示")
                    .setRationale("\"某APP\"需要使用相关权限,是否打开设置")
                    .setPositiveButton("是")
                    .setNegativeButton("否")
                    .build()
                    .show();
        }
    }

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

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode) {
            //当从软件设置界面,返回当前程序时候
            case AppSettingsDialog.DEFAULT_SETTINGS_REQ_CODE:
                requestPermissions(this);
                break;
        }
    }

    @AfterPermissionGranted(value = 0x99)
    public void checkPermissions() {
        String[] perms = {Manifest.permission.CAMERA, Manifest.permission.CHANGE_WIFI_STATE};
        if (EasyPermissions.hasPermissions(this, perms)) {
            //已经授权后的操作
        } else {
            //没有授权后的操作
            EasyPermissions.requestPermissions(this, getString(R.string.camera_rationale),
                    0x99, perms);
        }
    }


}

使用EasyPermissions时,需要实现它的PermissionCallbacks接口,这个接口提供了三个方法,其中onPermissionsGranted方法是授权成功后的回调,onPermissionsDenied方法是授权失败的回调,onRequestPermissionsResult方法是android.support.v4.app的ActivityCompat中的公共接口,当我们进行权限的请求时onRequestPermissionsResult方法会返回相应的请求结果,通过这个权限回调,可以对相应的操作做出响应。

EasyPermissions提供了hasPermissions方法,第二个参数传入的是一个权限数组,该方法会判断APP是否对这些权限进行过授权,如果其中有一个权限并没有被授权,最终结果会返回false,这时可以通过EasyPermissions的requestPermissions方法进行权限授权,其中requestCode是请求码,可以根据对应的请求码在授权回调中进行相应的处理,第三个可变参数就是我们需要授权的相关权限。具体的权限回调处理上面只是给出一个模板,具体的操作还是根据相关业务场景来调整。

EasyPermissions还提供了注解形式来处理授权后对应的requestCode处理,具体使用如下:

    @AfterPermissionGranted(value = 0x99)
    public void checkPermissions() {
        String[] perms = {Manifest.permission.CAMERA, Manifest.permission.CHANGE_WIFI_STATE};
        if (EasyPermissions.hasPermissions(this, perms)) {
            //已经授权后的操作
        } else {
            //没有授权后的操作
            EasyPermissions.requestPermissions(this, getString(R.string.camera_rationale),
                    0x99, perms);
        }
    }

源码解析

    public static boolean hasPermissions(Context context, @NonNull String... perms) {
        //Android版本判断
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            Log.w(TAG, "hasPermissions: API version < M, returning true by default");
            return true;
        }

        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;
    }

hasPermissions方法比较简单,先是对Android版本进行判断,小于Android 6.0的版本直接检查通过,只有大于Android 6.0时才会对权限列表进行检查,这里使用了ContextCompat的checkSelfPermissions方法进行权限检查,检查的结果不为0说明未授权。

在Android源码中,权限的检查最终会调用到 ContextImp中的checkPermission方法,源码如下:

    @Override
    public int checkPermission(String permission, int pid, int uid) {
        if (permission == null) {
            throw new IllegalArgumentException("permission is null");
        }

        try {
            return ActivityManagerNative.getDefault().checkPermission(
                    permission, pid, uid);
        } catch (RemoteException e) {
            return PackageManager.PERMISSION_DENIED;
        }
    }

在ContextImp中的checkPermission方法中,调用了ActivityManagerNative的getDefault()方法来获取ActivityManagerService的对象。ActivityManagerService 的职责是管理着应用程序中创建的所有组件(Activity、Service等),实现组件的管理,每个组件的状态变化都需要通知AMS,组件间的跨进程通信(IPC)是由ActivityManagerService 来操作的。

ActivityManagerNative中的getDefault方法如下:

    static public IActivityManager getDefault() {
        return gDefault.get();
    }
    
    private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }};
    
    static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;    
        }
        IActivityManager in =
                (IActivityManager) obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }
        return new ActivityManagerProxy(obj);
    }

在上面的create方法中通过ServiceManager.getService(“activity”)获取到ActivityManagerService对象,ActivityManagerService继承了 ActivityManagerNative,而 ActivityManagerNative继承了Binder并实现了IActivityManager接口,因此ActivityManagerService也是一个Binder对象,在asInterface方法中创建远程代理ActvityManagerProxy对象,经过一系列调用,最终调用ActivityManagerService的checkPermission方法。

    int checkComponentPermission(String permission, int pid, int uid,
                                 int owningUid, boolean exported) {
        // We might be performing an operation on behalf of an indirect binder
        // invocation, e.g. via {@link #openContentUri}.  Check and adjust the
        // client identity accordingly before proceeding.
        Identity tlsIdentity = sCallerIdentity.get();
        if (tlsIdentity != null) {
            Slog.d(TAG, "checkComponentPermission() adjusting {pid,uid} to {"
                    + tlsIdentity.pid + "," + tlsIdentity.uid + "}");
            uid = tlsIdentity.uid;
            pid = tlsIdentity.pid;
        }

        if (pid == MY_PID) {
            return PackageManager.PERMISSION_GRANTED;
        }

        return ActivityManager.checkComponentPermission(permission, uid,
                owningUid, exported); // 调用ActivityManager的checkComponentPermission来检查权限
    }
    @Override
    public int checkPermission(String permission, int pid, int uid) {
        if (permission == null) {
            return PackageManager.PERMISSION_DENIED;
        }

        // 调用checkComponentPermission函数来检查权限

        return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true);
    }

调用ActivityManager的checkComponentPermission方法:

    ./base/core/java/android/app/ActivityManager.java
    public static int checkComponentPermission(String permission, int uid,
                                               int owningUid, boolean exported) {
    ........
        try {
            // 调用AppGlobals的getPackageManager()函数返回IPackageManager对象
            return AppGlobals.getPackageManager()
                    .checkUidPermission(permission, uid);
        } catch (RemoteException e) {
            // Should never happen, but if it does... deny!
            Slog.e(TAG, "PackageManager is dead?!?", e);
        }
        return PackageManager.PERMISSION_DENIED;
    }

    ./base/core/java/android/app/AppGlobals.java
    public static IPackageManager getPackageManager() {
        return ActivityThread.getPackageManager(); // 调用ActivityThread的getPackageManager()函数
    }
    ./base/core/java/android/app/ActivityThread.java
    public static IPackageManager getPackageManager() {
        if (sPackageManager != null) {
            //Slog.v("PackageManager", "returning cur default = " + sPackageManager);
            return sPackageManager;
        }
        IBinder b = ServiceManager.getService("package");
        //Slog.v("PackageManager", "default service binder = " + b);
        sPackageManager = IPackageManager.Stub.asInterface(b);
        //Slog.v("PackageManager", "default service = " + sPackageManager);
        return sPackageManager;
    }

AppGlobals的getPackageManager方法中,通过ActivityThread的getPackageManager方法获取PackageManageService对象。
接着通过PackageManageService的checkUidPermission方法来检查权限。

    ./base/services/core/java/com/android/server/pm/PackageManagerService.java
    @Override
    public int checkUidPermission(String permName, int uid) {
        synchronized (mPackages) {
            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
            if (obj != null) {
                GrantedPermissions gp = (GrantedPermissions) obj;
                if (gp.grantedPermissions.contains(permName)) {
                    return PackageManager.PERMISSION_GRANTED;
                }
            } else {
                HashSet<String> perms = mSystemPermissions.get(uid);
                if ( perms != null &;&;
                perms.contains(permName)){
                    return PackageManager.PERMISSION_GRANTED;
                }
            }
        }
        return PackageManager.PERMISSION_DENIED;
    }

PackageManageService的checkUidPermission方法中,通过Settings的getUserIdLPr方法根据UID获取权限列表获,如果存在授权的权限返回PackageManager.PERMISSION_GRANTED,表示授权成功。

hasPermissions方法算是分析完毕了,这里在分析该方法的同时也梳理了权限检查的流程。

在分析EasyPermissions的requestPermissions方法前,先介绍一下几个重要的辅助类。

PermissionHelper是一个抽象类(模板方法),内部定义了几个抽象方法:

@RestrictTo(RestrictTo.Scope.LIBRARY)
public abstract class PermissionHelper<T> {
    
    .....

    public abstract void directRequestPermissions(int requestCode, @NonNull String... perms);

    public abstract boolean shouldShowRequestPermissionRationale(@NonNull String perm);

    public abstract void showRequestPermissionRationale(@NonNull String rationale,
                                                        @StringRes int positiveButton,
                                                        @StringRes int negativeButton,
                                                        int requestCode,
                                                        @NonNull String... perms);
    public abstract Context getContext();

}

shouldShowRequestPermissionRationale方法:如果应用之前请求过此权限但用户拒绝了请求,并在权限请求系统对话框中没有选择了 Don’t ask again 选项,此方法将返回 true。如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 Don’t ask again 选项,此方法将返回 false。如果设备规范禁止应用具有该权限,此方法也会返回 false。

directRequestPermissions方法:进行权限请求。

showRequestPermissionRationale方法:显示权限弹框,具体逻辑有子类实现。

getContext方法:获取对应的上下文。

很显然PermissionHelper是权限操作的抽象类,具体实现由子类来实现,请求权限时,可以在Activity、android.support.v4.app.Fragment或是android.app.Fragment中执行请求,PermissionHelper提供了创建这几种情况下的实现类。

@NonNull
public static PermissionHelper newInstance(Activity host) {
    if (Build.VERSION.SDK_INT < 23) {
        return new LowApiPermissionsHelper(host);
    }

    return new ActivityPermissionHelper(host);
}

@NonNull
public static PermissionHelper newInstance(Fragment host) {
    if (Build.VERSION.SDK_INT < 23) {
        return new LowApiPermissionsHelper(host);
    }

    return new SupportFragmentPermissionHelper(host);
}

@NonNull
public static PermissionHelper newInstance(android.app.Fragment host) {
    if (Build.VERSION.SDK_INT < 23) {
        return new LowApiPermissionsHelper(host);
    }

    return new FrameworkFragmentPermissionHelper(host);
}

如果权限授权在小于Android 6.0的版本时,会创建LowApiPermissionsHelper对象,LowApiPermissionsHelper实现了PermissionHelper的几个抽象方法。

class LowApiPermissionsHelper extends PermissionHelper<Object> {

    public LowApiPermissionsHelper(@NonNull Object host) {
        super(host);
    }

    @Override
    public void directRequestPermissions(int requestCode, @NonNull String... perms) {
        throw new IllegalStateException("Should never be requesting permissions on API < 23!");
    }

    @Override
    public boolean shouldShowRequestPermissionRationale(@NonNull String perm) {
        return false;
    }

    @Override
    @SuppressLint("NewApi")
    public void showRequestPermissionRationale(@NonNull String rationale,
                                               int positiveButton,
                                               int negativeButton,
                                               int requestCode,
                                               @NonNull String... perms) {
        throw new IllegalStateException("Should never be requesting permissions on API < 23!");
    }

    @Override
    public Context getContext() {
        return null;
    }
}

LowApiPermissionsHelper的几个方法都是空的,这是因为在API小于23时,不需要对运行时权限做处理。

ActivityPermissionHelper和FrameworkFragmentPermissionHelper针对的是在android.app包的Actvity和Fragment。

ActivityPermissionHelper:

class ActivityPermissionHelper extends BaseFrameworkPermissionsHelper<Activity> {

    public ActivityPermissionHelper(Activity host) {
        super(host);
    }

    @Override
    @SuppressLint("NewApi")
    public FragmentManager getFragmentManager() {
        return getHost().getFragmentManager();
    }

    @Override
    public void directRequestPermissions(int requestCode, @NonNull String... perms) {
        ActivityCompat.requestPermissions(getHost(), perms, requestCode);
    }

    @Override
    public boolean shouldShowRequestPermissionRationale(@NonNull String perm) {
        return ActivityCompat.shouldShowRequestPermissionRationale(getHost(), perm);
    }

    @Override
    public Context getContext() {
        return getHost();
    }
}

FrameworkFragmentPermissionHelper:

class FrameworkFragmentPermissionHelper extends BaseFrameworkPermissionsHelper<Fragment> {

    public FrameworkFragmentPermissionHelper(@NonNull Fragment host) {
        super(host);
    }

    @Override
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)
    public FragmentManager getFragmentManager() {
        return getHost().getChildFragmentManager();
    }

    @Override
    @SuppressLint("NewApi")
    public void directRequestPermissions(int requestCode, @NonNull String... perms) {
        getHost().requestPermissions(perms, requestCode);
    }

    @Override
    @SuppressLint("NewApi")
    public boolean shouldShowRequestPermissionRationale(@NonNull String perm) {
        return getHost().shouldShowRequestPermissionRationale(perm);
    }

    @Override
    @SuppressLint("NewApi")
    public Context getContext() {
        return getHost().getActivity();
    }
}

ActivityPermissionHelper和FrameworkFragmentPermissionHelper实现逻辑都差不多,在Activity中请求权限会通过ActivityCompat的requestPermissions方法进行请求,在Fragment中会通过Fragment的requestPermissions方法进行权限请求。并且这两个Helper类都继承了BaseFrameworkPermissionsHelper类。

    public abstract class BaseFrameworkPermissionsHelper<T> extends PermissionHelper<T> {

        public BaseFrameworkPermissionsHelper(@NonNull T host) {
            super(host);
        }

        public abstract FragmentManager getFragmentManager();

        @Override
        @RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
        public void showRequestPermissionRationale(@NonNull String rationale,
                                                   int positiveButton,
                                                   int negativeButton,
                                                   int requestCode,
                                                   @NonNull String... perms) {
            RationaleDialogFragment
                    .newInstance(positiveButton, negativeButton, rationale, requestCode, perms)
                    .show(getFragmentManager(), RationaleDialogFragment.TAG);
        }
    }

BaseFrameworkPermissionsHelper类非常简单,就两个方法,其中一个抽象方法getFragmentManager获取FragmentManager对象,还有一个实现了PermissionHelper类中的抽象方法showRequestPermissionRationale,showRequestPermissionRationale方法会显示一个权限弹框。
具体的子类实现在PermissionHelper中已经介绍过,继续看support.v4.app包的下Fragment的权限操作(SupportFragmentPermissionHelper)。

 class SupportFragmentPermissionHelper extends BaseSupportPermissionsHelper<Fragment> {

        public SupportFragmentPermissionHelper(@NonNull Fragment host) {
            super(host);
        }

        @Override
        public FragmentManager getSupportFragmentManager() {
            return getHost().getChildFragmentManager();
        }

        @Override
        public void directRequestPermissions(int requestCode, @NonNull String... perms) {
            getHost().requestPermissions(perms, requestCode);
        }

        @Override
        public boolean shouldShowRequestPermissionRationale(@NonNull String perm) {
            return getHost().shouldShowRequestPermissionRationale(perm);
        }

        @Override
        public Context getContext() {
            return getHost().getActivity();
        }
    }

SupportFragmentPermissionHelper的逻辑与之前两个Helper类一样,不需要再介绍了。

继续分析requestPermissions方法,EasyPermissions提供了多个requestPermissions方法:

    public static void requestPermissions(
            @NonNull Activity host, @NonNull String rationale,
            int requestCode, @NonNull String... perms) {
        requestPermissions(host, rationale, android.R.string.ok, android.R.string.cancel,
                requestCode, perms);
    }

    public static void requestPermissions(
            @NonNull Fragment host, @NonNull String rationale,
            int requestCode, @NonNull String... perms) {

        requestPermissions(host, rationale, android.R.string.ok, android.R.string.cancel,
                requestCode, perms);
    }

    public static void requestPermissions(
            @NonNull android.app.Fragment host, @NonNull String rationale,
            int requestCode, @NonNull String... perms) {

        requestPermissions(host, rationale, android.R.string.ok, android.R.string.cancel,
                requestCode, perms);
    }

这个三个requestPermissions方法应该很熟悉了,权限弹框按钮的文案是默认的,要想设置权限弹框的文案,可以调用以下三种requestPermissions方法。

    public static void requestPermissions(
            @NonNull Activity host, @NonNull String rationale,
            @StringRes int positiveButton, @StringRes int negativeButton,
            int requestCode, @NonNull String... perms) {
        requestPermissions(PermissionHelper.newInstance(host), rationale,
                positiveButton, negativeButton,
                requestCode, perms);
    }

    public static void requestPermissions(
            @NonNull Fragment host, @NonNull String rationale,
            @StringRes int positiveButton, @StringRes int negativeButton,
            int requestCode, @NonNull String... perms) {
        requestPermissions(PermissionHelper.newInstance(host), rationale,
                positiveButton, negativeButton,
                requestCode, perms);
    }

    public static void requestPermissions(
            @NonNull android.app.Fragment host, @NonNull String rationale,
            @StringRes int positiveButton, @StringRes int negativeButton,
            int requestCode, @NonNull String... perms) {
        requestPermissions(PermissionHelper.newInstance(host), rationale,
                positiveButton, negativeButton,
                requestCode, perms);
    }

通过一系列调用,最后都是都用以下方法:

    private static void requestPermissions(
            @NonNull PermissionHelper helper, @NonNull String rationale,
            @StringRes int positiveButton, @StringRes int negativeButton,
            int requestCode, @NonNull String... perms) {
        //进行权限检查
        if (hasPermissions(helper.getContext(), perms)) {
            notifyAlreadyHasPermissions(helper.getHost(), requestCode, perms);
            return;
        }

        //请求权限
        helper.requestPermissions(rationale, positiveButton,
                negativeButton, requestCode, perms);
    }

方法第一个参数PermissionHelper就是前三个requestPermissions方法针对app包和suppor包下的Activity和Fragment权限操作类,也就是前面提到的那几个Helper类。在请求权限之前会检查之前权限有没有授权过,如果授权过调用notifyAlreadyHasPermissions方法。

    private static void notifyAlreadyHasPermissions(@NonNull Object object,
                                                    int requestCode,
                                                    @NonNull String[] perms) {
        int[] grantResults = new int[perms.length];
        for (int i = 0; i < perms.length; i++) {
            grantResults[i] = PackageManager.PERMISSION_GRANTED;
        }
        onRequestPermissionsResult(requestCode, perms, grantResults, object);
    }

notifyAlreadyHasPermissions方法非常简单,授权成功后将 grantResults 数组全部赋值未PackageManager.PERMISSION_GRANTED,再通过onRequestPermissionsResult方法将结果回调给客户端。继续看requestPermissions方法中的helper.requestPermissions方法,该方法就是调用app和support下Activity和Fragment的
shouldShowRequestPermissionRationale方法,该方法就是Android进行权限授权的方法,当进行授权操作后会回调onRequestPermissionsResult方法。如下:

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

拿到授权结果后通过EasyPermissions的onRequestPermissionsResult方法进行结果回调,onRequestPermissionsResult方法如下。

    public static void onRequestPermissionsResult(int requestCode,
                                                  @NonNull String[] permissions,
                                                  @NonNull int[] grantResults,
                                                  @NonNull Object... receivers) {
        //授权成功
        List<String> granted = new ArrayList<>();
        //授权失败或没授权
        List<String> denied = new ArrayList<>();
        for (int i = 0; i < permissions.length; i++) {
            String perm = permissions[i];
        }
        if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
            granted.add(perm);
        } else {
            denied.add(perm);
        }

        for (Object object : receivers) {
            // 授权成功
            if (!granted.isEmpty()) {
                if (object instanceof EasyPermissions.PermissionCallbacks) {
                    ((EasyPermissions.PermissionCallbacks) object).onPermissionsGranted(requestCode, granted);
                }
            }
            //未授权
            if (!denied.isEmpty()) {
                if (object instanceof EasyPermissions.PermissionCallbacks) {
                    ((EasyPermissions.PermissionCallbacks) object).onPermissionsDenied(requestCode, denied);
                }
            }
            //全部授权通过 
            if (!granted.isEmpty() && denied.isEmpty()) {
                runAnnotatedMethods(object, requestCode);
            }
        }
    }

方法中granted集合用于保存授权成功的权限,denied集合保存的是未授权或授权失败的权限,接着通过循环遍历权限请求类,对授权成功和未成功集合做判断,如果granted集合不为空说明授权成功,会回调onPermissionsGranted方法,如果denied集合不为空,说明有权限未授权成功,会回调onPermissionsDenied方法,当然如果granted集合不为空并且denied集合为空,说明百分百之一百授权成功,这时调用runAnnotatedMethods方法。

    private static void runAnnotatedMethods(@NonNull Object object, int requestCode) {
        Class clazz = object.getClass();
        if (isUsingAndroidAnnotations(object)) {
            clazz = clazz.getSuperclass();
        }
        while (clazz != null) {
            //遍历请求权限类中的方法
            for (Method method : clazz.getDeclaredMethods()) {
                //判断是否使用了AfterPermissionGranted注解
                if (method.isAnnotationPresent(AfterPermissionGranted.class)) {
                    AfterPermissionGranted ann = method.getAnnotation(AfterPermissionGranted.class);
                    if (ann.value() == requestCode) {
                        //无参方法
                        if (method.getParameterTypes().length > 0) {
                            throw new RuntimeException(
                                    "Cannot execute method " + method.getName() + " because it is non-void method and/or has input parameters.");
                        }
                        try {
                            // Make method accessible if private
                            if (!method.isAccessible()) {
                                method.setAccessible(true);
                            }
                            //反射调用AfterPermissionGranted注解的方法
                            method.invoke(object);
                        } catch (IllegalAccessException e) {
                            Log.e(TAG, "runDefaultMethod:IllegalAccessException", e);
                        } catch (InvocationTargetException e) {
                            Log.e(TAG, "runDefaultMethod:InvocationTargetException", e);
                        }
                    }
                   
                }
            }
            //获取父类的Class
            clazz = clazz.getSuperclass();
        }
    }

该方法主要对AfterPermissionGranted注解的方法进行处理,通过遍历请求权限类的方法,如果是通过AfterPermissionGranted注解的方法并且是无参的,通过请求的requestCode与该注解方法的value一样,就通过反射执行该方法,告之请求类权限请求成功。这里稍微提下,一开始会执行isUsingAndroidAnnotations方法进行判断是否获取直接继承父类的class,isUsingAndroidAnnotations方法如下:

    private static boolean isUsingAndroidAnnotations(@NonNull Object object) {
        if (!object.getClass().getSimpleName().endsWith("_")) {
            return false;
        }
        try {
            Class clazz = Class.forName("org.androidannotations.api.view.HasViews");
            return clazz.isInstance(object);
        } catch (ClassNotFoundException e) {
            return false;
        }
    }

其实这个方法也就是判断是否使用了 AndroidAnnotations这个注解框架,关于这个框架大家可以自行网上搜索。

之前看了onRequestPermissionsResult方法,也就是授权后的结果回调,现在看下权限请求的逻辑。

//请求权限
    helper.requestPermissions(rationale, positiveButton,
            negativeButton, requestCode, perms);

调用了PermissionHelper的requestPermissions方法。

    public boolean shouldShowRationale(@NonNull String... perms){
        for (String perm:perms){
            // 上次弹出权限点击了禁止(但没有勾选“下次不在询问”)
            if(shouldShowRequestPermissionRationale(perm)){
                return true;
            }
        }
        //第一次打开App时或者上次选择禁止并勾选“下次不在询问”
        return false;
    }

    public void requestPermissions(@NonNull String rationale,
                                   @StringRes int positiveButton,
                                   @StringRes int negativeButton,
                                   int requestCode,
                                   @NonNull String... perms){
        if(shouldShowRationale(perms)){
            // 上次弹出权限点击了禁止(但没有勾选“下次不在询问”)
            showRequestPermissionRationale(rationale,
                    positiveButton,
                    negativeButton,
                    requestCode,
                    perms);
        }else{
            //第一次打开App时或者上次选择禁止并勾选“下次不在询问”
            //进行权限请求
            directRequestPermissions(requestCode,perms);
        }
    }

requestPermissions方法中,当上次弹出权限点击了禁止(但没有勾选“下次不在询问”),这时需要弹出权限弹框,当第一次打开App时或者上次选择禁止并勾选“下次不在询问”,这时进行权限请求。shouldShowRequestPermissionRationale、showRequestPermissionRationale和directRequestPermissions都是抽象方法,具体实现由子类实现,关于这三个方法在上面已经介绍过了。
结合上面的一系列分析,权限请求后,通过回调onPermissionsGranted和onPermissionsDenied方法告之请求类请求结果,比如当我们选择禁止并勾选“下次不在询问”时,这时请求授权失败,回调onPermissionsDenied方法:

    @Override
    public void onPermissionsDenied(int requestCode, List<String> perms) {
        /**
         * 若是在权限弹窗中,用户勾选了'NEVER ASK AGAIN.'或者'不在提示',且拒绝权限。
         * 这时候,需要跳转到设置界面去,让用户手动开启。
         */
        if(requestCode==124) {
            if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {
                new AppSettingsDialog
                        .Builder(this)
                        .setTitle("\"火链财经\"权限提示")
                        .setRationale("\"火链财经\"需要使用相关权限,是否打开设置")
                        .setPositiveButton("是")
                        .setNegativeButton("否")
                        .build()
                        .show();
            } else {
                requestPermissions(this);
            }
        }
    }

可以针对具体业务进行操作,就像上面若是在权限弹窗中,用户勾选了’NEVER ASK AGAIN.‘或者’不在提示’,且拒绝权限。 这时候,需要跳转到设置界面去,让用户手动开启。否则再次请求授权。
到这里EasyPermissions分析分析完毕。

©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:上身试试 返回首页