• Android-注解处理器


                                  Android-Java注解处理器

    基本概念

    注解处理器(Annotation Processor)是javac的一个工具。它用来在编译时扫描和处理注解(Annotation)。你能够对自己定义注解,并注冊对应的注解处理器。到这里,我如果你已经知道什么是注解,而且知道怎么申明的一个注解。

    假设你不熟悉注解,你能够在这官方文档中得到很多其它信息。

    注解处理器在Java 5開始就有了,可是从Java 6(2006年12月公布)開始才有可用的API。

    过了一些时间,Java世界才意识到注解处理器的强大作用,所以它到近期几年才流行起来。

    一个注解的注解处理器,以Java代码(或者编译过的字节码)作为输入。生成文件(一般是.java文件)作为输出。这详细的含义什么呢?你能够生成Java代码。这些生成的Java代码是在生成的.java文件里,所以你不能改动已经存在的Java类。比如向已有的类中加入方法。这些生成的Java文件,会同其它普通的手动编写的Java源码一样被javac编译。

    使用注解处理器

    以下来讲述怎么在Android Studio中使用注解处理器,
    在这里就须要介绍一个gradle插件android-apt了,
    官方地址: https://bitbucket.org/hvisser/android-apt

    Android Studio原本是不支持注解处理器的, 可是用这个插件后, 我们就能够使用注解处理器了, 这个插件能够自己主动的帮你为生成的代码创建文件夹, 让生成的代码编译到APK里面去, 并且它还能够让终于编译出来的APK里面不包括注解处理器本身的代码, 由于这部分代码仅仅是编译的时候须要用来生成代码, 终于执行的时候是不须要的。

    使用这个插件非常easy, 首先在你项目顶层的build.gradle文件里加入依赖项, 例如以下:

    buildscript {
        repositories {
            jcenter()
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:1.5.0'
            classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
        }
    }

    然后在app的build.gradle里面加入插件的引用以及须要依赖哪些库, 例如以下:

    apply plugin: 'com.android.application'
    apply plugin: 'com.neenbedankt.android-apt'
    ...
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        testCompile 'junit:junit:4.12'
        compile 'com.android.support:appcompat-v7:22.2.1'
        compile 'la.dahuo:command:1.0.0'
        apt 'la.dahuo:command-codegen:1.0.0'
    }

    注意上面的apt ‘la.dahuo:command-codegen:1.0.0’, 这里表示引用一个注解处理器的库, 这个库的代码终于不会进入编译出来的APK里面。

    使用注解

    以下就来介绍怎么使用注解生成代码, 上面出现的库la.dahuo:command,
    la.dahuo:command-codegen就是我依据命令设计模式(Command Design Pattern)写的一个注解库, 它用来让我们方便的生成代码, 看下使用方法:

    定义Command

    @CommandDef("commandName")
    public class MyCommand extends Command {
       
       @Param
       String paramStr;
       
       @Param
    ("paramIntName")
       int paramInt;
       
       @Override
       public void execute() {
           // do something with params    } }

    上面的代码通过@CommandDef(“commandName”)来标注MyCommand是一个Command类, 它相应的名字为commandName. 然后通过标注@Param来定义了两个參数, @Param不填值的话表示參数名和属性名一样, 也能够通过填入一个值来指定參数名

    使用Command

    Map<String, Object> params = new HashMap<>();
    params.put("paramStr", "string");
    params.put("paramIntName", 1);
    Command.parse("commandName", params).execute();

    分析

    看完上面的使用方法, 你可能会认为就这么多代码就能够跑了吗?

    不须要再加入什么代码了?
    我的答案是: 确实能够跑, 不须要加入其它代码了。
    有木有认为用起来非常方便?

    我们以下来分析下这是怎么做到的。

    首先标注处理器会依据@Param来获取Command的结构信息, 然后用来生成CommandFactory, 生成的代码在build/generated/source/apt以下, 生成的CommandFactory例如以下:

    public class MyCommandFactory implements CommandFactory {
       public Command newCommand(Map<String, Object> params) {        MyCommand command = new MyCommand();        command.paramStr = TypeConverter.toString(params.get("paramStr"));        command.paramInt = TypeConverter.toInt(params.get("paramIntName"));
           return command;    } }

    有了CommandFactory, 我们就能够通过Mapparams来构造Command了.

    那Command是怎么自己主动注冊的呢? 那我们就要看另外一个生成的类了.

    public class CommandRegisters {
       public static void register(Map<String, CommandFactory> map) {
           map.put("myCommand", new MyCommandFactory());    } }

    它依据标注@CommandDef来获取了全部Command类的信息, 然后完毕全部Command的注冊.

    有了上面自己主动生成的代码, 那么解析Command就非常easy了. 代码例如以下:

    public static Command parse(String action, Map<String, Object> params) {
        CommandFactory factory = CommandRepository.getInstance().getFactory(action);
       if (factory != null) {
           return factory.newCommand(params);    }
       return EmptyCommand.INSTANCE; }

    CommandRepository类通过反射的方式调用CommandRegisters类的register方法来完毕全部Command的注冊. 这是我这个注解库唯一的一处使用反射的地方, Command的创建都通过CommandFactory来完毕, 性能非常高, 比那些通过大量反射来实现对象映射的库性能好非常多(比方GSON, 能够实现json转object), 并且也不须要依赖非常多代码, 依赖一个非常小的库
    la.dahuo:command就够了, 才5K.

    怎么创建注解处理器

    注解处理器最核心的就是要有一个Processor, 它继承自AbstractProcessor,它长成这个样子:

    package com.example;

    public class MyProcessor extends AbstractProcessor {
       
       @Override
       public synchronized void init(ProcessingEnvironment env){ }

       @Override
       public boolean process(Set<?

    extends TypeElement> annoations, RoundEnvironment env) { }

       @Override
       public Set<String> getSupportedAnnotationTypes() { }
       
       @Override
       public SourceVersion getSupportedSourceVersion() { } }

    • init(ProcessingEnvironment env):每一个注解处理器都必须有个空的构造方法。

      只是,有一个特殊的init方法,它会被注解处理器工具传入一个ProcessingEnvironment作为參数来调用。

      ProcessingEnvironment提供了一些实用的工具类,如Elements。Types和Filter。

      我们后面会用到它们。

    • process(Set<?

      extends TypeElement> annotations, RoundEnvironment env):这种方法能够看做每一个处理器的main方法。你要在这里写下你的扫描。推断和处理注解的代码。并生成java文件。

      通过传入的RoundEnvironment參数,你能够查询被某个特定注解注解的元素,我们稍后会看到。

    • getSupportedAnnotationTypes( ):这里你须要说明这个处理器须要针对哪些注解来注冊。注意返回类型是一个字符串的Set,包括了你要用这个处理器处理的注解类型的全名。

    • getSupportedSourceVersion( ):用于指定你使用的java版本号。通常你会返回SourceVersion.latestSupported( )。

    接下来你须要知道的一件事就是注解处理器在它自己的JVM中执行。是的,你没看错。javac启动一个完整的Java虚拟机来给注解处理器执行。这对你意味着什么?你能够使用你在不论什么其它java应用中使用的东西,比方谷歌的guava!假设你愿意。你还能够使用依赖注入工具,如dagger或不论什么你想要使用的库。只是不要忘了。虽然这仅仅是个小的处理器,你依旧须要考虑高效的算法和设计模式,就像是你会为不论什么其它Java应用所做的那样。

    注冊你的处理器

    你或许会问自己:”我该如何把我的处理器注冊到javac?”。

    你须要提供一个.jar文件。就像其它的jar文件一样,你要把编译后的处理器打包到那个文件。

    并且你还须要把一个特别的放在META-INF/services下的叫做javax.annotation.processing.Processor的文件打包进你的.jar文件。

    所以你的.jar文件内容看起来会像这个样子:

    MyProcessor.jar
        - com
            - example
                - MyProcessor.class
        - META-INF
            - services
                - javax.annotation.processing.Processor

    java.annotation.processing.Processor文件(打包到MyProcessor.jar中)的内容是用换行符隔开的处理器完整类名:

    com.example.MyProcessor
    com.foo.OtherProcessor
    net.blabla.SpecialProcessor

    这样, 一个注解处理器的框架就好了, 完毕代码后编译成jar文件, 然后像開始介绍的那样加入依赖就好了.


    gitbub原地址:https://github.com/derron/command-lib#rd

  • 相关阅读:
    java图形化Swing教程(一)
    NYOJ467 中缀式变后缀式 【栈】
    火云开发课堂
    Egret项目Typescript的编译报错
    Java学习笔记(八):集合类
    Java学习笔记(七):内部类、静态类和泛型
    认识JavaScript的原型
    Java学习笔记(六):面向对象、接口和抽象类
    Java学习笔记(五):异常处理
    Java学习笔记(四):流程控制
  • 原文地址:https://www.cnblogs.com/yangykaifa/p/7258515.html
Copyright © 2020-2023  润新知