Android 6.0版本以上运行时权限解析(二)

本文是关于Android 6.0及以上版本运行时权限的解析系列的第二篇,重点介绍了如何使用Java Annotation Processor优化之前使用反射的方法,以提高性能。文章通过代码示例展示了Annotation Processor如何收集信息和生成代码,最终生成一个辅助类以实现PermissionProxy接口,提升运行效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一篇文章我们主要介绍了在Android 6.0版本以上运行时权限的相关知识,并将使用方式进行了封装。但目前使用了Java注解+反射的方式实现,考虑到反射会影响到性能,下面将使用Java Annotation Processor处理(在此假设已经对Annotation Processor有了一些了解并会使用)。

对外暴露的接口还是和原来一样, 先上一段源码吧。

@AutoService(Processor.class)
public class PermissionProcessor extends AbstractProcessor {

    private Elements elementUtils;
    private Filer mFiler;
    private Messager mMessager;

    Map<String, ProxyInfo> classMap = new HashMap<>();


    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        elementUtils = processingEnv.getElementUtils();
        mFiler = processingEnv.getFiler();
        mMessager = processingEnv.getMessager();
    }


    @Override
    public Set<String> getSupportedAnnotationTypes() {
        HashSet<String> supportTypes = new LinkedHashSet<>();
        supportTypes.add(PermissionSuccess.class.getCanonicalName());
        supportTypes.add(PermissionFail.class.getCanonicalName());
        return supportTypes;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        classMap.clear();
        if (!gatherInformation(roundEnv, PermissionSuccess.class)) return false;
        if (!gatherInformation(roundEnv, PermissionFail.class)) return false;

        generateClassFile();
        return true;
    }


    private boolean gatherInformation(RoundEnvironment roundEnv, Class<? extends Annotation> clazz) {
        Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(clazz);
        for (Element element : elements) {
            // 检测合法性
            if (!checkMethodValid(element, clazz)) return false;
            // 因为是方法,将element 转为ExecutableElement对象
            ExecutableElement annotatedMethod = (ExecutableElement) element;
            // class type
            TypeElement classElement = (TypeElement) annotatedMethod.getEnclosingElement();
            // class full name
            String classFullName = classElement.getQualifiedName().toString();

            ProxyInfo proxyInfo = classMap.get(classFullName);
            if (proxyInfo == null) {
                proxyInfo = new ProxyInfo(elementUtils, classElement);
                classMap.put(classFullName, proxyInfo);
                proxyInfo.setTypeElement(classElement);
            }
            // 获取注解
            Annotation annotation = annotatedMethod.getAnnotation(clazz);
           // 判断注解类型
            if (annotation instanceof PermissionSuccess) {
                int requestCode = ((PermissionSuccess) annotation).requestCode();
                // 如果是PermissionSuccess注解,将方法名存入grantedMethodMap中,requestCode作为键
                proxyInfo.grantedMethodMap.put(requestCode, annotatedMethod.getSimpleName().toString());
            } else if (annotation instanceof PermissionFail) {
                int requestCode = ((PermissionFail) annotation).requestCode();
                proxyInfo.deniedMethodMap.put(requestCode, annotatedMethod.getSimpleName().toString());
            } else {
                error(element, "%s not support.", clazz.getSimpleName());
                return false;
            }
        }
        return true;
    }

    private void generateClassFile() {
        // 遍历classMap
        for (String key : classMap.keySet()) {
            // 取出收集到的信息
            ProxyInfo proxyInfo = classMap.get(key);

            try {
                mMessager.printMessage(Diagnostic.Kind.NOTE, "ClassFullName: " + proxyInfo.getProxyFullClassName());
                JavaFileObject object = mFiler.createSourceFile(proxyInfo.getProxyFullClassName(), proxyInfo.getTypeElement());

                Writer writer = object.openWriter();
                // 生成Java代码
                writer.write(proxyInfo.generateJavaCode());
                writer.flush();
                writer.close();
            } catch (IOException e) {
                error(proxyInfo.getTypeElement(),
                        "Unable to write injector for type %s: %s",
                        proxyInfo.getTypeElement(), e.getMessage());
            }
        }


    }

    private void error(Element element, String message, Object... args) {
        if (args.length > 0) {
            message = String.format(message, args);
        }
        mMessager.printMessage(Diagnostic.Kind.NOTE, message, element);
    }

    private boolean checkMethodValid(Element annotatedElement, Class clazz) {
        if (annotatedElement.getKind() != ElementKind.METHOD) {
            error(annotatedElement, "%s must be declared on method.", clazz.getSimpleName());
            return false;
        }
        if (ClassValidator.isPrivate(annotatedElement) || ClassValidator.isAbstract(annotatedElement)) {
            error(annotatedElement, "%s() must can not be abstract or private.", annotatedElement.getSimpleName());
            return false;
        }

        return true;
    }
}

process方法主要有两个功能:

  1. 收集信息
  2. 生成代码

收集信息的步骤在注释做了解释。接下来看生成代码的部分;

public String generateJavaCode() {

        StringBuilder builder = new StringBuilder();
        builder.append("// Generated code. Do not modify!\n");
        builder.append("package ").append(packageName).append(";\n\n");
        builder.append("import com.muse.permission.*;\n");
        builder.append('\n');

        builder.append("public class ").append(proxyClassName).append(" implements " + ProxyInfo.PROXY + "<" + typeElement.getSimpleName() + ">");
        builder.append(" {\n");

        generateMethods(builder);
        builder.append('\n');

        builder.append("}\n");
        return builder.toString();


    }

    private void generateMethods(StringBuilder builder) {
        generateSuccessMethod(builder);
        generateFailMethod(builder);
    }

    private void generateFailMethod(StringBuilder builder) {
        builder.append("@Override\n ");
        builder.append("public void denied(" + typeElement.getSimpleName() + " source , int requestCode) {\n");
        builder.append("switch(requestCode) {\n");
        for (int code : deniedMethodMap.keySet()) {
            builder.append("case " + code + ":\n");
            builder.append("source." + deniedMethodMap.get(code) + "();");
            builder.append("break;");
        }

        builder.append("}\n");
        builder.append("  }\n");
    }

    private void generateSuccessMethod(StringBuilder builder) {
        builder.append("@Override\n ");
        builder.append("public void grant(" + typeElement.getSimpleName() + " source , int requestCode) {\n");
        builder.append("switch(requestCode) {\n");
        for (int code : grantedMethodMap.keySet()) {
            builder.append("case " + code + ":\n");
            builder.append("source." + grantedMethodMap.get(code) + "();");
            builder.append("break;");
        }

        builder.append("}\n");
        builder.append("  }\n");

    }

这里生成了一个辅助类,该类实现了PermissionProxy接口。接口定义如下:

public interface PermissionProxy<T> {
    void grant(T source, int requestCode);

    void denied(T source, int requestCode);
}
以上generateSuccessMethod 方法和 generateFailMethod 方法分别是针对grant 接口方法 和 denied 接口方法的实现。使用了拼接字符串的方式实现了,还可以使用javapoet这里库实现,这里就先不做解释了,对此感兴趣的可以参考这篇文章: JavaPoet的基本使用

下面看看生成的辅助类是什么样子;

import com.muse.permission.PermissionProxy;

public class MainActivity$$PermissionProxy implements PermissionProxy<MainActivity> {
    public MainActivity$$PermissionProxy() {
    }

    public void grant(MainActivity source, int requestCode) {
        switch(requestCode) {
        case 1:
            source.openSucc();
            break;
        case 2:
            source.test1();
        }

    }

    public void denied(MainActivity source, int requestCode) {
        switch(requestCode) {
        case 1:
            source.openFail();
            break;
        case 2:
            source.test2();
        }

    }
}

好了,讲解就到此吧!

又到周末了... “小伙子,‘快应用’ 了解下?”大笑



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值