美文网首页
打造一个简易的编译时注解框架(一)

打造一个简易的编译时注解框架(一)

作者: 梦止惰 | 来源:发表于2017-04-26 15:21 被阅读126次

    一、AbstractProcessor

    1,概述

    注解分为编译时注解和运行时注解,现在流行的主流框架ButterKnife,Retrofit,Dragger都是编译时注解。编译时注解的核心依赖APT(Annotation Processing Tools)实现的,原理是在某些代码元素上(如类型、函数、字段等)添加注解,在编译时编译器会检查AbstractProcessor的子类,并且调用process函数,然后将添加了注解的所有元素都传递到process函数中,使得开发人员可以在编译器进行相应的处理。

    二、创建步骤

    1,首先使用Android Studio创建一个Android的project。然后开始创建一个名为processor的java library。 点击file->new->new module如图

    Paste_Image.png
    我们需要创建一个非Android的library,注意一定要选择Java Library
    这里我们取名叫ButterKnifeProcessor

    2,兼容性配置

    在app的工程下的build.gradle,进行配置

    Paste_Image.png

    加入这句话

    compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_7
    targetCompatibility JavaVersion.VERSION_1_7
    }

    如图:

    Paste_Image.png

    3,创建Annotation

    创建annotation的Java moduel,里面放置注解类,如图:

    Paste_Image.png

    4,创建注解处理器

    在process模块下创建一个处理器,这个继承AbstractProcessor,如图:

    Paste_Image.png Paste_Image.png

    这个类上可以添加注解:
    @SupportedAnnotationTypes的值为当前类支持的注解的完整类路径,支持通配符。如图也就是注解类的类路径
    @SupportedSourceVersion 标识该处理器支持的源码版本
    该类的源码:

    @SupportedAnnotationTypes("com.example.BindView")
    @SupportedSourceVersion(SourceVersion.RELEASE_7)
    public class ButterKnifeProcessor extends AbstractProcessor {
    
        @Override
        public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
            StringBuilder builder = new StringBuilder()
                    .append("package com.example.generated;\n\n")
                    .append("public class GeneratedClass {\n\n") // open class
                    .append("\tpublic String getMessage() {\n") // open method
                    .append("\t\treturn \"");
    
    
            // for each javax.lang.model.element.Element annotated with the CustomAnnotation
            for (Element element : roundEnv.getElementsAnnotatedWith(BindView.class)) {
                String objectType = element.getSimpleName().toString();
                // this is appending to the return statement
                builder.append(objectType).append(" says hello!\\n");
            }
    
            builder.append("\";\n") // end return
                    .append("\t}\n") // close method
                    .append("}\n"); // close class
    
            try { // write the file
                JavaFileObject source = processingEnv.getFiler().createSourceFile("com.example.generated.GeneratedClass");
                Writer writer = source.openWriter();
                writer.write(builder.toString());
                writer.flush();
                writer.close();
            } catch (IOException e) {
                // Note: calling e.printStackTrace() will print IO errors
                // that occur from the file already existing after its first run, this is normal
            }
            return true;
        }
    }```
    
    ***这里的process方法生成了一个Java文件,在上面的方法中我们想在这个生成这个类和类中的方法,也就是上面StringBuilder 里面连接的字符串,用来测试,这里先不要管这个类是怎么生成的后面会说的***
    

    package com.example.generated;

    public class GeneratedClass {

    public String getMessage() {
        return "mTextView says hello!\nmTextView1 says hello!\n";
    }
    

    }```

    5,创建resource文件

    创建好注解处理器后,我们需要告诉编译器在编译的时候使用哪个注解处理器,这里就需要创建javax.annotation.processing.Processor文件在processor模块下,main目录中创建一个resources文件夹,然后下边在创建META-INF/services,最后里边一个javax.annotation.processing.Processor文件,如下:

    Paste_Image.png

    这个txt文件下写下注解器的路径,如:

    Paste_Image.png

    选择上图中红色部分,就能将这个类的路径copy出来

    6,添加android-apt

    在project下的build.gradle中添加apt插件

    Paste_Image.png

    添加依赖:

    classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

    再在app的build.gradle中添加

    apply plugin: 'com.neenbedankt.android-apt'

    Paste_Image.png

    7,设置build的依赖

    注解处理器编译生成一个jar,然后把这个jar包复制到app模块下。然后让app依赖引用这个jar。
    首先配置app的build.gradle依赖项

    dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
    exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.3.0'
    compile 'com.android.support.constraint:constraint-layout:1.0.0-beta5'
    compile project(':annotation')
    apt project(':processor')
    testCompile 'junit:junit:4.12'
    }

    然后我们编写一个gradle task,把生成的jar文件复制到app/libs目录中。

    task processorTask(type: Copy) {
    from '../processor/build/libs/processor.jar' into 'libs/'
    }
    processorTask.dependsOn(':processor:build')
    preBuild.dependsOn(processorTask)

    这里的processor是Java moduel的名字,换成你自己的名字即可
    完整的build:

    Paste_Image.png

    8,使用注解

    Paste_Image.png

    先执行Clean Project,然后再执行ReBuild Project,如果BUILD SUCCESSFUL
    当然也可以在libs中看到生成的jar文件
    如图:

    Paste_Image.png

    然后我们可以在下述位置查看到生成的Java文件

    app/build/generated/source/apt/debug/package/GeneratedClass.java

    如图:

    Paste_Image.png

    此时这个类的代码是这个样的

    Paste_Image.png

    是不是在此时,觉得很神奇,怎么在ButterKnifeProcessor 中process中StringBuilder连接的字符串怎么在这里生成了,
    返回去看那个类的源码,是不是思路一下的就清晰啦,通过反射注解,编译器在 rebuild的时候就会检查ButterKnifeProcessor 这个类,调用process方法,所以效率是很高的

    9,验证是否能使用

    在app中的MainActivity中使用,代码如下:

    public class MainActivity extends AppCompatActivity {
        @BindView(R.id.textView)
        TextView mTextView;
        @BindView(R.id.textView1)
        TextView mTextView1;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
        
         showMessage();
    
        }
    
        private void showMessage() {
            GeneratedClass generatedClass = new GeneratedClass();
            String message = generatedClass.getMessage();
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setMessage(message)
                    .setTitle("测试编译时")
                    .setPositiveButton("ok", null).show();
        }
    }
    
    Paste_Image.png
    下一篇:
    http://www.jianshu.com/p/c516036506fd
    参考文档
    http://blog.stablekernel.com/the-10-step-guide-to-annotation-processing-in-android-studio

    相关文章

      网友评论

          本文标题:打造一个简易的编译时注解框架(一)

          本文链接:https://www.haomeiwen.com/subject/vodizttx.html