zoukankan      html  css  js  c++  java
  • apt 根据注解,编译时生成代码

    apt:

    @Retention后面的值,设置的为CLASS,说明就是编译时动态处理的。一般这类注解会在编译的时候,根据注解标识,动态生成一些类或者生成一些xml都可以,在运行时期,这类注解是没有的~~会依靠动态生成的类做一些操作,因为没有反射,效率和直接调用方法没什么区别~~~

    RUNTIME, 说明就是运行时动态处理,这个大家见得应该最多,在运行时拿到类的Class对象,然后遍历其方法、变量,判断有无注解声明,然后做一些事情。

    SOURCE,标记一些信息,这么说可能太抽象,那么我说,你见过@Override、@SuppressWarnings等,这类注解就是用于标识,可以用作一些检验 

    @Target表示该注解可以用于什么地方,可能的类型TYPE(类),FIELD(成员变量)

     1 public enum ElementType {  
     2     /** 
     3      * Class, interface or enum declaration. 
     4      */  
     5     TYPE,  
     6     /** 
     7      * Field declaration. 
     8      */  
     9     FIELD,  
    10     /** 
    11      * Method declaration. 
    12      */  
    13     METHOD,  
    14     /** 
    15      * Parameter declaration. 
    16      */  
    17     PARAMETER,  
    18     /** 
    19      * Constructor declaration. 
    20      */  
    21     CONSTRUCTOR,  
    22     /** 
    23      * Local variable declaration. 
    24      */  
    25     LOCAL_VARIABLE,  
    26     /** 
    27      * Annotation type declaration. 
    28      */  
    29     ANNOTATION_TYPE,  
    30     /** 
    31      * Package declaration. 
    32      */  
    33     PACKAGE  
    34 } 

    TypeElement  :类

    JavaPoet源码初探

    TypeSpec是类型元素的抽象,通过Kind枚举定义class、interface、enum、annotation四种类型。

    MethodSpec代表方法的抽象。

    1     // 元素操作的辅助类
    2     Elements elementUtils;
    3     //文件相关的辅助类
    4     private Filer mFiler;
    5     //日志相关的辅助类
    6     private Messager mMessager;

    1、先添加两个java library,一个annotation,一个compiler:

    annotation library:用于放置注解。

    build.gradle文件:注意属于java library,不使用android library。

    1  apply plugin: 'java'
    2 
    3  sourceCompatibility = 1.7
    4  targetCompatibility = 1.7
    5  dependencies {
    6      compile fileTree(dir: 'libs', include: ['*.jar'])
    7  }

    compile library:用于放置根据注解生成代码类。

    build.gradle文件:注意属于java library,不使用android library。

     1 apply plugin: 'java'
     2 
     3 sourceCompatibility = 1.7
     4 targetCompatibility = 1.7
     5 dependencies {
     6     compile fileTree(dir: 'libs', include: ['*.jar'])
     7 
     8     compile 'com.google.auto.service:auto-service:1.0-rc2'
     9     compile 'com.squareup:javapoet:1.7.0'
    10     <!-- 引用annotation library  -->
    11     compile project(':annotation')
    12 }

    app module:android app

    build.gradle文件:

     1 apply plugin: 'com.android.application'
     2 apply plugin: 'com.neenbedankt.android-apt'
     3 
     4 android {
     5     compileSdkVersion 23
     6     buildToolsVersion "23.0.2"
     7 
     8     defaultConfig {
     9         applicationId "com.example.aptdemo"
    10         minSdkVersion 15
    11         targetSdkVersion 23
    12         versionCode 1
    13         versionName "1.0"
    14     }
    15     buildTypes {
    16         release {
    17             minifyEnabled false
    18             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    19         }
    20     }
    21 }
    22 
    23 dependencies {
    24     compile fileTree(dir: 'libs', include: ['*.jar'])
    25     testCompile 'junit:junit:4.12'
    26     compile 'com.android.support:appcompat-v7:24.0.0'
    27 
    28     compile project(':annotation')
    29     apt project(':compiler')
    30 }

    官方有一个列子的:生成一个java类,然后java 类里面有个main函数

    // 注解类 (目录:annotation library)

    package com.example;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.CLASS)
    public @interface Test {
    }

    // apt生成类 (目录:compiler library)

     1 package com.example;
     2 
     3 import com.google.auto.service.AutoService;
     4 import com.squareup.javapoet.JavaFile;
     5 import com.squareup.javapoet.MethodSpec;
     6 import com.squareup.javapoet.TypeSpec;
     7 import java.io.IOException;
     8 import java.util.Collections;
     9 import java.util.Set;
    10 import javax.annotation.processing.AbstractProcessor;
    11 import javax.annotation.processing.Processor;
    12 import javax.annotation.processing.RoundEnvironment;
    13 import javax.lang.model.SourceVersion;
    14 import javax.lang.model.element.Modifier;
    15 import javax.lang.model.element.TypeElement;
    16 
    17 @AutoService(Processor.class)
    18 public class TestProcessor extends AbstractProcessor {
    19 
    20     /***
    21      package com.example.helloworld;
    22 
    23      public final class HelloWorld {
    24         public static void main(String[] args) {
    25             System.out.println("Hello, JavaPoet!");
    26         }
    27      }
    28      */
    29 
    30     @Override
    31     public Set<String> getSupportedAnnotationTypes() {
    32         return Collections.singleton(Test.class.getCanonicalName());
    33     }
    34 
    35     @Override
    36     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    37         
    38         MethodSpec main = MethodSpec.methodBuilder("main")
    39                 .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
    40                 .returns(void.class)
    41                 .addParameter(String[].class, "args")
    42                 .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
    43                 .build();
    44 
    45         TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
    46                 .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
    47                 .addMethod(main)
    48                 .build();
    49 
    50         JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
    51                 .build();
    52 
    53         try {
    54             javaFile.writeTo(processingEnv.getFiler());
    55         } catch (IOException e) {
    56             e.printStackTrace();
    57         }
    58 
    59         return false;
    60     }
    61 
    62     @Override
    63     public SourceVersion getSupportedSourceVersion() {
    64         return SourceVersion.RELEASE_7;
    65     }
    66 }
    官方例子

    // 使用:

    在随意一个类上面加上@Test,编译时,代码就会生成。

    下面就是简单的view注解生成代码:

    // 注解类 (目录:annotation library)

    1、activity 注解 :作用于类,所以目标类型是类。

    1 @Target(ElementType.TYPE)
    2 @Retention(RetentionPolicy.CLASS)
    3 public @interface DIActivity {
    4 }

    2、view 注解:作用于字段,所以目标类型是字段。

    1 @Target(ElementType.FIELD)
    2 @Retention(RetentionPolicy.RUNTIME)
    3 public @interface DIView {
    4 
    5     int value() default 0;
    6 }

    3、onClick 注解:作用于方法,所以目标类型是方法。

    1 @Target(ElementType.METHOD)
    2 @Retention(RetentionPolicy.RUNTIME)
    3 public @interface DIOnClick {
    4 
    5     int[] value() default 0;
    6 }

    // 使用工具类:

    ViewInject:用于统一activity 使用方法,注入。

     1 public class ViewInject {
     2 
     3     public final static void bind(Activity activity){
     4 
     5         String className = activity.getClass().getName();
     6         try {
     7             Class<?> aClass = Class.forName(className + "$ViewInject");
     8             Inject inject = (Inject) aClass.newInstance();
     9             inject.bindView(activity);
    10         } catch (Exception e){
    11             e.printStackTrace();
    12         }
    13     }
    14 }

    Inject:用于apt生成类继承,统一父类。

    1 public interface Inject<T> {
    2 
    3     void bindView(T host);
    4 }

    // apt生成类 (目录:compiler library)

    DIProcessor:

      1 @AutoService(Processor.class)
      2 public class DIProcessor extends AbstractProcessor {
      3 
      4     // 元素操作的辅助类
      5     Elements elementUtils;
      6     //文件相关的辅助类
      7     private Filer mFiler;
      8     //日志相关的辅助类
      9     private Messager mMessager;
     10 
     11     @Override
     12     public synchronized void init(ProcessingEnvironment processingEnv) {
     13         super.init(processingEnv);
     14         // 元素操作的辅助类
     15         elementUtils = processingEnv.getElementUtils();
     16         mFiler = processingEnv.getFiler();
     17         mMessager = processingEnv.getMessager();
     18     }
     19 
     20     /**
     21      * 指定哪些注解应该被注解处理器注册
     22      * @return
     23      */
     24     @Override
     25     public Set<String> getSupportedAnnotationTypes() {
     26         /**
     27          * Set<String> types = new LinkedHashSet<>();
     28          types.add(BindView.class.getCanonicalName());
     29          types.add(OnClick.class.getCanonicalName());
     30          return types;
     31          * */
     32         // 规定需要处理的注解
     33         return Collections.singleton(DIActivity.class.getCanonicalName());
     34     }
     35 
     36     @Override
     37     public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
     38 
     39         Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(DIActivity.class);
     40 
     41         for(Element element : elements){
     42 
     43             // 判断是否Class
     44             TypeElement typeElement = (TypeElement) element;
     45 
     46             List<? extends Element> members = elementUtils.getAllMembers(typeElement);
     47 
     48             MethodSpec.Builder bindViewMethod = MethodSpec.methodBuilder("bindView")
     49                     .addModifiers(Modifier.PUBLIC)
     50                     .returns(TypeName.VOID)
     51                     .addParameter(ClassName.get(typeElement.asType()), "activity", Modifier.FINAL);
     52 
     53             bindViewMethod.addStatement("mContext = activity");
     54 
     55             MethodSpec.Builder onClickMethod = MethodSpec.methodBuilder("onClick")
     56                     .addAnnotation(Override.class)
     57                     .addModifiers(Modifier.PUBLIC)
     58                     .returns(TypeName.VOID)
     59                     .addParameter(ClassName.get("android.view", "View"), "view");
     60 
     61 //            TypeSpec.Builder listener = TypeSpec.anonymousClassBuilder("")
     62 //                    .addSuperinterface(ClassName.get("android.view", "View", "OnClickListener"));
     63 
     64             mMessager.printMessage(Diagnostic.Kind.NOTE, "非常厉害");
     65 
     66 //            bindViewMethod.addStatement("View.OnClickListener listener = null");
     67             onClickMethod.addCode("switch(view.getId()) {");
     68 
     69             for (Element item : members) {
     70                 // handler the findViewById
     71                 DIView diView = item.getAnnotation(DIView.class);
     72                 if (diView != null){
     73 
     74                     bindViewMethod.addStatement(String.format("activity.%s = (%s) activity.findViewById(%s)"
     75                             ,item.getSimpleName(), ClassName.get(item.asType()).toString(), diView.value()));
     76                     continue;
     77                 }
     78 
     79                 // handler the setOnViewClick
     80                 DIOnClick diOnClick = item.getAnnotation(DIOnClick.class);
     81                 if(diOnClick != null) {
     82 
     83                     if(diOnClick.value().length > 1) {
     84                         for (int index = 0; index < diOnClick.value().length; index++) {
     85                             onClickMethod.addCode("case $L: ", diOnClick.value()[index]);
     86 
     87                             bindViewMethod.addStatement("activity.findViewById($L).setOnClickListener(this)", diOnClick.value()[index]);
     88                         }
     89 
     90                         onClickMethod.addStatement("mContext.$N()", item.getSimpleName());
     91 
     92                         onClickMethod.addStatement("break");
     93                     }
     94 
     95                     if(diOnClick.value().length == 1) {
     96                         onClickMethod.addCode("case $L: ", diOnClick.value()[0]);
     97                         onClickMethod.addStatement("mContext.$N()", item.getSimpleName());
     98                         onClickMethod.addStatement("break");
     99 
    100                         bindViewMethod.addStatement("activity.findViewById($L).setOnClickListener(this)", diOnClick.value()[0]);
    101                     }
    102                     continue;
    103                 }
    104             }
    105 
    106             onClickMethod.addStatement("}");
    107 
    108 //            TypeSpec onClickListener = listener.addMethod(onClickMethod.build()).build();
    109 //            bindViewMethod.addStatement("listener = $L ", onClickListener);
    110 
    111             List<MethodSpec> methodSpecs = new ArrayList<>();
    112             methodSpecs.add(bindViewMethod.build());
    113             methodSpecs.add(onClickMethod.build());
    114 
    115             List<TypeName> typeNames = new ArrayList<>();
    116             typeNames.add(ClassName.get("android.view", "View", "OnClickListener"));
    117             typeNames.add(ParameterizedTypeName.get(ClassName.get("com.example.charles.aptdemo.inject", "Inject"), TypeName.get(typeElement.asType())));
    118 
    119             FieldSpec fieldSpec = FieldSpec.builder(TypeName.get(typeElement.asType()), "mContext").build();
    120 
    121             TypeSpec typeSpec = TypeSpec.classBuilder(element.getSimpleName() + "$ViewInject")
    122 //                    .superclass(TypeName.get(typeElement.asType()))
    123                     .addSuperinterfaces(typeNames)
    124                     .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
    125                     .addMethods(methodSpecs)
    126                     .addField(fieldSpec)
    127                     .build();
    128 
    129             JavaFile javaFile = JavaFile.builder(getPackageName(typeElement), typeSpec).build();
    130 
    131             try {
    132                 javaFile.writeTo(processingEnv.getFiler());
    133             } catch (IOException e) {
    134                 e.printStackTrace();
    135             }
    136 
    137             mMessager.printMessage(Diagnostic.Kind.NOTE, "完成");
    138         }
    139 
    140         return true;
    141     }
    142 
    143     private String getPackageName(TypeElement type) {
    144         return elementUtils.getPackageOf(type).getQualifiedName().toString();
    145     }
    146 
    147     /**
    148      * 指定使用的 Java 版本。通常返回SourceVersion.latestSupported()。
    149      * @return
    150      */
    151     @Override
    152     public SourceVersion getSupportedSourceVersion() {
    153         return SourceVersion.RELEASE_7;
    154     }
    155 }
    DIProcessor

    // 使用方式:

     1 @DIActivity
     2 public class MainActivity extends AppCompatActivity {
     3 
     4     @DIView(R.id.text)
     5     public TextView textView;
     6 
     7     @Override
     8     protected void onCreate(Bundle savedInstanceState) {
     9         super.onCreate(savedInstanceState);
    10         setContentView(R.layout.activity_main);
    11 
    12         ViewInject.bind(this);
    13 
    14         textView.setText("我不是这样子的");
    15     }
    16 
    17     @DIOnClick({R.id.btn_change_text, R.id.btn_change})
    18     public void changeTextView(){
    19         textView.setText("我被点击了");
    20     }
    21 
    22     @DIOnClick(R.id.btn_change_new)
    23     public void changeNew(){
    24         textView.setText("new");
    25     }
    26 }

    关于javapoet:

    JavaPoet 是一个用来生成 .java源文件的Java API。

    下列文章为apt

    Android注解-编译时生成代码 (APT)

    下列文章为反射:

     Android 打造编译时注解解析框架 这只是一个开始

    Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (上)

    Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (下)

  • 相关阅读:
    JavaScript浏览器对象模型(BOM)之location对象
    JavaScript浏览器对象模型(BOM)之window对象
    8-python模拟登入(无验证码)
    7-python自定义opener
    6-豆瓣剧情排行爬虫
    2-chrome无法添加扩展程序
    5-有道爬虫demo(post)
    4-fiddler抓包中文乱码:
    3-百度贴吧爬虫
    2-python代码坑点
  • 原文地址:https://www.cnblogs.com/CharlesGrant/p/5811338.html
Copyright © 2011-2022 走看看