Android9.0动态运行时权限源码分析及封装改造<四>-----打造自己的权限申请框架下

跟离上一次https://www.cnblogs.com/webor2006/p/13344912.html的权限申请框架学习又过去一年多了,还差这篇的收尾,这次把它给完结了,不能半途而废。

编译时自动生成文件:

生成代码编写:

我们已经把所有标有注解的方法都收集起来了,接下来则可以根据咱们收集的这些信息来编写代码生成的逻辑,回到这块:

要想生成代码,需要使用到这个对象:

然后接下来则遍历咱们收集的方法集合来进行类的生成,如下:

其中MethodInfo中有几个私有方法需要将其公开化:

编译看效果:

annotationProcessor注册:

此时需要回到app module中,这样注册一下:

编译:

接下来编译看一下会不会报错,报错了,而且是一个经典的错:

com.android.tools.r8.a: Invoke-customs are only supported starting with Android O (--min-api 26)

解决起来也很简单,在gradle中配置一个JDK版本为8既可,如下:

再次编译,发现完全木有生成。。其实是这里的依赖有点问题,更改一下:

再次编译,类成功生成:

,不错还是报了个错:

呃,生成的类怎么首字母多了一个“$”呢? 其实是这块写得有问题:

这里这样修改就可以了:

然后还发现生成方法时的代码写得有点问题,顺便也给改了:

再编译,文件生成正常了:

很明显少了一个参数了,所以又报错了。。

解决也很简单,把接口定义改一下:

然后咱们在生成代码处也得对应的修改一下:

再编译,就正常啦:

abstractProcessor远程断点调试:

在继续往下进行代码编写之前,这里先暂停学习一个开发技巧,我们也知道在编写AnnotationProcessor如果出现问题是很难定位错误的对吧,其实它也是有调试技巧的,所以接下来看一下如何进行它的代码调试。

1、gradle.properties文件下增加:

org.gradle.daemon=true
org.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5105

2、项目的teminal中运行gradle --deamon开启守护进程:

(base) xiongweideMacBook-Pro:permissionstudy xiongwei$ ./gradlew --daemon
Starting a Gradle Daemon, 4 incompatible Daemons could not be reused, use --status for details

> Task :help

Welcome to Gradle 6.1.1.

To run a build, run gradlew <task> ...

To see a list of available tasks, run gradlew tasks

To see a list of command-line options, run gradlew --help

To see more detail about a task, run gradlew help --task <task>

For troubleshooting, visit https://help.gradle.org

BUILD SUCCESSFUL in 10s
1 actionable task: 1 executed

我本机gradle的版本为:

 

3、配置调试程序:

 

点击ok之后,在运行这块需要切换至此:

 

4、点击调试启动按钮,然后在项目的terminal中运行gradle clean assembleDebug运行项目:

然后打几个断点:

 

然后再执行一下它:

此时看一下效果:

成功debug了,这样对于这块的问题排查就有一个非常好的方式了。

权限请求类库封装:

目前注解处理类的逻辑已经处理好了,接下来则需要封装咱们自己的权限请求类来调用它们了。

1、将libpermissionhelper改为module:

目前它是一个java library:

为啥要改呢?因为它里面需要用到Android的一个类,比如Activity,Frgment,如下:

2、新建文件:

package com.permissionstudy.libpermissionhelper;

import android.app.Activity;

import androidx.fragment.app.Fragment;

public class PermissionHelper {
    public static void requestPermission(Activity activity, String[] permission, int requestCode) {
        doRequestPermission(activity, permission, requestCode);
    }

    public static void requestPermission(Fragment fragment, String[] permission, int requestCode) {
        doRequestPermission(fragment.getActivity(), permission, requestCode);
    }

    private static void doRequestPermission(Activity activity, String[] permission, int requestCode) {
        //TODO
    }
}

3、6.0以下系统直接通过:

我们知道,Android的动态权限是在6.0以后才提出的,所以对于6.0及以下的默认都是全通过的,所以先来处理这个条件:

那它里面具体逻辑呢?其实就是应该找到注解处理器扫描自动生成的这个类:

代码也比较容易,直接贴出:

package com.permissionstudy.libpermissionhelper;

import android.app.Activity;
import android.os.Build;

import androidx.fragment.app.Fragment;

public class PermissionHelper {
    private static final String SUFFIX = "$$PermissionProxy";

    public static void requestPermission(Activity activity, String[] permission, int requestCode) {
        doRequestPermission(activity, permission, requestCode);
    }

    public static void requestPermission(Fragment fragment, String[] permission, int requestCode) {
        doRequestPermission(fragment.getActivity(), permission, requestCode);
    }

    private static void doRequestPermission(Activity activity, String[] permission, int requestCode) {
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
            //6.0及以下系统直接通过
            doExecuteGrant(activity, requestCode, permission);
            return;
        }
    }

    //授权成功
    private static void doExecuteGrant(Activity activity, int requestCode, String[] permission) {
        PermissionProxy proxy = findProxy(activity);
        proxy.granted(requestCode, activity, permission);
    }

    //找到Activity注解处理器生成的类
    private static PermissionProxy findProxy(Activity activity) {
        Class<? extends Activity> aClass = activity.getClass();
        try {
            Class<?> forName = Class.forName(aClass.getName() + SUFFIX);
            PermissionProxy proxy = (PermissionProxy) forName.newInstance();
            return proxy;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }
}

4、处理6.0以后系统权限:

不需要弹窗提示:

接下来则来处理6.0以后系统的权限逻辑了,当然是需要主动申请才行,而申请时可能想弹一个说明框,所以先做一个是否要弹框说明的逻辑:

需要弹窗提示:

首先对用户已经拒绝的权限进行一个弹窗,所以需要从用户申请权限中先要过滤出已经被用户拒绝的权限,如下:

那当有需要解释的拒绝权限,那怎么弹窗呢?其实是想调用到Activity的这个方法:

所以注解处理器生成的代理类就发挥作用了,如下:

也就是会调到这:

另外,此提示弹窗显示之后,当用户点击“好的”按钮时,应该再次发起被拒权限的申请,所以现在的问题Activity的PermissionHelper类怎么进行一个交互,很显然用接口回调的方式,新建一个接口:

然后PermissionProxy.rational()接口中就得增加一个这个回调参数了:

 

它一动,一系列的连锁反应也就来了,首先是Activity:

然后,在注解代码生成器中也得变一下:

然后再PermissionHelper的调用处就可以这样写了:

这里重新编译一下,确保代理类没问题:

真正发起权限申请:

逻辑也比较简单:

运行: 

一切就绪,接下来咱们调用一下咱们的封装类,来测度一下权限申请的效果:

注意,记得对应的在Manifest.xml中也声明一下,不然你要申请的权限是不会真正进行申请的:

当权限被拒绝时,咱们应该也给用户一个提示,如下:

而当权限被用户拒绝之后,则需要给出一个已拒绝权限的列表:

另外,在这个回调中,也得把逻辑让咱们的PermissionHelper来处理了:

其处理逻辑为:

其中这里面增加了一个方法:

好,接下来咱们来运行看一下效果,发现没任何效果。。原因是有个笔误:

修改一下:

那,再运行,发现还是不对。。

我全新安装的APP,而且机型是大于6.0的系统,咋就直接提示申请成功了。。有问题,这时因为咱们的PermissionHelper有几个代码还是写得有问题,下面先来调整一下:

对于需要弹框的权限和真正发起权限申请的权限是需要区分开的:

好,下面再运行看一下:

嗯,木问题,弹出了我们测试时的三个权限的申请框了,接下来分几种场景来测一下,看有没有其它的bug:

1、允许一个权限,拒绝剩下的二个权限:

 

这里看出一个问题了,我明显是允许了写入sdcard的权限,拒绝了相机和手机状态读取的权限,为啥在权限授权提示中,貌似反了:

但是,如果我退出APP再进来,再提示就对了:

那接下来解决我们在授权时的结果提示的这个问题,经过debug,发现原因了:

其原因就是这个判断条件出问题了:

改为它:

此时重新来一次,app卸载安装一下:

嗯,修复了,不过还有一个细节,这里再调整一下,就是对于申请成功的权限,我们也把哪些申请成功的权限打出来,目前只提示了一个“申请成功”:

再运行:

2、再打开app,看拒绝的权限是否会再弹框提示?

那,此时我再退出app,重新打开app,那之前咱们拒绝的权限还会再次被弹窗提示出来么?试一下:

木问题。

3、把剩下的所有权限都允许:

接下来这一步又出问题了,点击“好的”,发现死循环了:

原因也是由于笔误,改一下:

应该是改成它:

好,再次运行看一下:

貌似完美了,最后再整体走一遍流程:

总结:

至此,整个权限申请框架就已经编写完成了,回忆一下编译时的流程:

 

而运行时的流程为:

最后把PermissionHelper类的整个源码贴出来供参考:

package com.permissionstudy.libpermissionhelper;

import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;

import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;

import java.util.ArrayList;
import java.util.List;

public class PermissionHelper {
    private static final String SUFFIX = "$$PermissionProxy";

    public static void requestPermission(Activity activity, String[] permission, int requestCode) {
        doRequestPermission(activity, permission, requestCode);
    }

    public static void requestPermission(Fragment fragment, String[] permission, int requestCode) {
        doRequestPermission(fragment.getActivity(), permission, requestCode);
    }

    private static void doRequestPermission(Activity activity, String[] permission, int requestCode) {
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
            //6.0及以下系统直接通过
            doExecuteGrant(activity, requestCode, permission);
            return;
        }
        boolean rational = shouldShowPermissionRational(activity, requestCode, permission);
        if (rational) {
            //如果需要弹窗,则下面的真正发起权限申请的逻辑则不应该执行
            return;
        }
        doRealRequestPermission(activity, permission, requestCode);
    }

    //真正发起权限申请
    private static void doRealRequestPermission(Activity activity, String[] permission, int requestCode) {
        List<String> deniedPermissions = findDeniedPermissions(activity, permission);
        if (deniedPermissions.size() > 0) {
            String[] denied = new String[deniedPermissions.size()];
            deniedPermissions.toArray(denied);
            ActivityCompat.requestPermissions(activity, denied, requestCode);
        } else {
            //如果没有需要授权的权限,则就代表所有权限都申请成功了
            doExecuteGrant(activity, requestCode, permission);
        }
    }

    private static boolean shouldShowPermissionRational(final Activity activity, final int requestCode, final String[] permission) {
        PermissionProxy proxy = findProxy(activity);
        //需要弹框说明:将用户已经拒绝的权限给出提示
        List<String> shouldShowRationalePermissions = findShouldShowRationalePermissions(activity, permission);
        if (!shouldShowRationalePermissions.isEmpty()) {
            //调用到Activity中被@PermissionRational注解修饰的方法
            String[] denied = new String[shouldShowRationalePermissions.size()];
            shouldShowRationalePermissions.toArray(denied);
            return proxy.rational(requestCode, activity, denied, new PermissionRationalCallback() {
                @Override
                public void onRationalExecute() {
                    //重新发起被用户拒绝了的权限申请
                    doRealRequestPermission(activity, permission, requestCode);
                }
            });
        }
        return false;
    }

    //从用户申请的权限中过滤出需要弹窗提示的权限
    private static List<String> findShouldShowRationalePermissions(Activity activity, String[] permissions) {
        List<String> rational = new ArrayList<>();
        for (String permission : permissions) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
                //系统判断一下看是否该权限需要解释,需要解释的才放到集合中
                rational.add(permission);
            }
        }
        return rational;
    }

    //从用户申请的权限中过滤出已经拒绝了的
    private static List<String> findDeniedPermissions(Activity activity, String[] permissions) {
        List<String> deniedPermissions = new ArrayList<>();
        for (String permission : permissions) {
            if (ActivityCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED) {
                //系统判断一下看是否该权限需要解释,需要解释的才放到集合中
                deniedPermissions.add(permission);
            }
        }
        return deniedPermissions;
    }

    //授权成功
    private static void doExecuteGrant(Activity activity, int requestCode, String[] permission) {
        PermissionProxy proxy = findProxy(activity);
        proxy.granted(requestCode, activity, permission);
    }

    //授权失败
    private static void doExecuteDenied(Activity activity, int requestCode, String[] permission) {
        PermissionProxy proxy = findProxy(activity);
        proxy.denied(requestCode, activity, permission);
    }

    //找到Activity注解处理器生成的类
    private static PermissionProxy findProxy(Activity activity) {
        Class<? extends Activity> aClass = activity.getClass();
        try {
            Class<?> forName = Class.forName(aClass.getName() + SUFFIX);
            PermissionProxy proxy = (PermissionProxy) forName.newInstance();
            return proxy;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }

    //权限结果回调
    public static void onRequestPermissionsResult(Activity activity, int requestCode, String[] permissions, int[] grantResults) {
        List<String> grantPermissions = new ArrayList<>();
        List<String> deniedPermissions = new ArrayList<>();
        for (int i = 0; i < grantResults.length; i++) {
            String permission = permissions[i];
            if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                deniedPermissions.add(permission);
            } else {
                grantPermissions.add(permission);
            }
        }
        if (grantPermissions.size() > 0) {
            String[] grant = new String[grantPermissions.size()];
            grantPermissions.toArray(grant);
            doExecuteGrant(activity, requestCode, grant);
        }
        if (deniedPermissions.size() > 0) {
            String[] denied = new String[deniedPermissions.size()];
            deniedPermissions.toArray(denied);
            doExecuteDenied(activity, requestCode, denied);
        }
    }
}

posted on 2020-08-27 10:12  cexo  阅读(117)  评论(0编辑  收藏  举报

导航