1.9 annotation 在Android中应用

使用注解标记元数据,可用于在工程编译期间,解析注解标记的元素,自动生成java文件和代码。

常用的三个工具

annotationProcessor

annotationProcessor和android-apt的功能是一样的,它们是替代关系。是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,根据注解自动生成代码。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。

AutoService

主要的作用是注解 processor 类,并对其生成 META-INF 的配置信息。

META-INF 的配置信息作用:相当于一个信息包,目录中的文件和目录获得Java 2平台的认可与解释,用来配置应用程序、扩展程序、类加载器和服务

JavaPoet

这个库的主要作用就是帮助我们通过类调用的形式来生成代码。

实践

目的:
通过注解Activity中的字段,生成初始化代码,类似butterKnife的@bindView注解的作用

第一步:声明注解

在项目中新建一个叫做annotationlib的Java library,用来定义我们在项目中需要使用到的注解,这个lib下面的代码会直接打包到我们项目中

DIActivity:用来修饰类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface DIActivity {
}

DIView:用来修饰字段

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DIView {
    int value() default 0;
}

第二步:解析注解,生成代码

再次新建一个Java Library,命名为compilerlib,这个库的主要作用:

  • 项目在编译期间,扫描项目中用到的注解,然后解析注解,生成Java代码
  • 该项目会以annotationProcessor 的方式依赖到项目中,不会参与实际的打包
  1. build.gradle文件添加需要用到第三方库
    //主要的作用是注解 processor 类,并对其生成 META-INF 的配置信息。
    implementation 'com.google.auto.service:auto-service:1.0-rc3'
    //主要作用就是帮助我们通过类调用的形式来生成代码。
    implementation 'com.squareup:javapoet:1.10.0'
    api project(':annotationlib')
  1. DIProcessor 解析注解,生成Java文件以及代码
@AutoService(Processor.class)
public class DIProcessor extends AbstractProcessor {
    private Elements elementUtils;
    @Override
    public Set getSupportedAnnotationTypes() {
        // 规定需要处理的注解
        return Collections.singleton(DIActivity.class.getCanonicalName());
    }
    @Override
    public boolean process(Set annotations, RoundEnvironment roundEnv) {
        System.out.println("DIProcessor");
        Set elements = roundEnv.getElementsAnnotatedWith(DIActivity.class);
        for (Element element : elements) {
            // 判断是否Class
            TypeElement typeElement = (TypeElement) element;
            List members = elementUtils.getAllMembers(typeElement);
            MethodSpec.Builder bindViewMethodSpecBuilder = MethodSpec.methodBuilder("bindView")
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                    .returns(TypeName.VOID)
                    .addParameter(ClassName.get(typeElement.asType()), "activity");
            for (Element item : members) {
                DIView diView = item.getAnnotation(DIView.class);
                if (diView == null){
                    continue;
                }
                bindViewMethodSpecBuilder.addStatement(String.format("activity.%s = (%s) activity.findViewById(%s)",item.getSimpleName(),ClassName.get(item.asType()).toString(),diView.value()));
            }
            TypeSpec typeSpec = TypeSpec.classBuilder("DI" + element.getSimpleName())
                    .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                    .addMethod(bindViewMethodSpecBuilder.build())
                    .build();
            JavaFile javaFile = JavaFile.builder(getPackageName(typeElement), typeSpec).build();
            try {
                javaFile.writeTo(processingEnv.getFiler());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }
    private String getPackageName(TypeElement type) {
        return elementUtils.getPackageOf(type).getQualifiedName().toString();
    }
    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        elementUtils = processingEnv.getElementUtils();
    }
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.RELEASE_7;
    }
}

第三步:使用注解

在MainActivity中使用注解:@DIActivity , @DIView()

@DIActivity
public class MainActivity extends AppCompatActivity {

    @DIView(R.id.text1)
    TextView textView;
    @DIView(R.id.text2)
    TextView textView2;
    @DIView(R.id.text3)
    TextView textView3;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //由compilerlib 生成的
        DIMainActivity.bindView(this);
        textView.setText("Hello Worldaaaaaaaaaaaa");
    }
}

DIMainActivity是由compilerlib在编译期间生成,代码如下:

public final class DIMainActivity {
  public static void bindView(MainActivity activity) {
    activity.textView = (android.widget.TextView) activity.findViewById(2131165316);
    activity.textView2 = (android.widget.TextView) activity.findViewById(2131165317);
    activity.textView3 = (android.widget.TextView) activity.findViewById(2131165318);
  }
}
完整demo

你可能感兴趣的:(1.9 annotation 在Android中应用)