Android自定义注解

Posted by Chris on March 27, 2018

[源代码][1] [1]: https://github.com/chris118/android-annotation

###1) 建一个Java库来专门放注解,库名为:ptannotation, 注意,这里必须为Java库,不然会找不到javax包下的相关资源

定义注解类 PTRoutRule.java

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface PTRoutRule {
    String[] value();
}

###2) 建一个Java库工程,库名为:ptcompiler,记得是和存放注解的库分开的。注意,这里必须为Java库,不然会找不到javax包下的相关资源

引入auto-service 和 javapoet

定义 PTProcessor.java

    implementation 'com.google.auto.service:auto-service:1.0-rc3'//生成 META-INF/services/javax.annotation.processing.Processor 文件
    implementation 'com.squareup:javapoet:1.8.0' //用于产生 .java 源文件的辅助库
    implementation project(':ptannotation')
@AutoService(Processor.class)
public class PTProcessor  extends AbstractProcessor {
    private static final boolean DEBUG = true;
    private Messager messager;
    private Filer filer;

    /**
     * init()方法会被注解处理工具调用,并输入ProcessingEnviroment参数。
     * ProcessingEnviroment提供很多有用的工具类Elements, Types 和 Filer
     * @param processingEnvironment 提供给 processor 用来访问工具框架的环境
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        messager = processingEnv.getMessager();
        filer = processingEnv.getFiler();
    }

    /**
     * 这里必须指定,这个注解处理器是注册给哪个注解的。注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称
     * @return  注解器所支持的注解类型集合,如果没有这样的类型,则返回一个空集合
     */
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> ret = new HashSet<>();
        ret.add(PTRoutRule.class.getCanonicalName());
        return ret;
    }

    /**
     * 指定使用的Java版本,通常这里返回SourceVersion.latestSupported(),默认返回SourceVersion.RELEASE_6
     * @return  使用的Java版本
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    /**
     * 这相当于每个处理器的主函数main(),你在这里写你的扫描、评估和处理注解的代码,以及生成Java文件。 
     * 输入参数RoundEnviroment,可以让你查询出包含特定注解的被注解元素 
     * @param annotations   请求处理的注解类型 
     * @param roundEnvironment  有关当前和以前的信息环境 
     * @return  如果返回 true,则这些注解已声明并且不要求后续 Processor 处理它们; 
     *          如果返回 false,则这些注解未声明并且可能要求后续 Processor 处理它们 
     */
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
        debug("process start------------" + annotations.toString());
        if (annotations.isEmpty()) {
            return false;
        }

        //添加init函数
        MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("init")
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
                .addCode("\n");


        // process PTRoutRule
        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(PTRoutRule.class);
        for (Element element : elements) {
            PTRoutRule router = element.getAnnotation(PTRoutRule.class);

            String url = null;
            for (String urlValue : router.value()) {
                debug(urlValue); //暂时支持一个url
                url = urlValue;
            }

            //添加注解的类
            ClassName className = null;
            if (element.getKind() == ElementKind.CLASS) {
                className = ClassName.get((TypeElement) element);
                debug(className.simpleName());
            }

            if(url == null){
                debug("url is null");
            }else if(className == null){
                debug("className is null");
            }else{
                if (element.getKind() == ElementKind.CLASS) {
                    debug("com.hh.ptrouter.PTRouter.Register:" + " url = " + url + " className = " + className);
                    methodBuilder.addStatement("com.hh.ptrouter.PTRouter.Register($S, $T.class)", url, className);
                }
            }
            methodBuilder.addCode("\n");
        }

        //创建类
        TypeSpec routerMapping = TypeSpec.classBuilder("PTRouterProcessor")
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addMethod(methodBuilder.build())
                .build();
        //写入文件
        try {
            JavaFile.builder("com.hh.ptrouter", routerMapping)
                    .build()
                    .writeTo(filer);
        } catch (Throwable e) {
            e.printStackTrace();
        }

        debug("process end------------" + annotations.toString());
        return true;
    }

    private void error(String error) {
        messager.printMessage(Diagnostic.Kind.ERROR, error);
    }

    private void debug(String msg) {
        if (DEBUG) {
            messager.printMessage(Diagnostic.Kind.NOTE, msg);
        }
    }
}

###3) App工程使用注解

    annotationProcessor project(':ptcompiler') //替代旧的apt
    implementation project(':ptannotation')
@PTRoutRule(value = "putao://home")
public class HomeActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
    }
}

###4) 生成文件

Make Project, 在Gradle Console中可以看到具体的调试打印信息,如果以上都成功了,会在目录/Users/admin/src/android/PTApp/app/build/generated/source/apt/debug/com/hh/ptrouter 生成PTRouterProcessor.java