Java注解知多少之自定义注解处理器

Java注解知多少之自定义注解处理器

前言

要知道如何自定义注解处理器(简称:APT),首先还是先得知道注解与注解处理器是怎么一会儿事儿,关于自定义注解,可以看我之前的文章,现在主要讲的是注解处理器的使用。

简单来说注解处理器其实就是用来处理注解用的,在编译的运行成class文件的时候,注解处理器会根据需要处理的注解执行一些操作。

下面我们来自定义个跟ButterKnife类似的注解处理器来了解他的大致用法。

一、自定义注解

https://www.jianshu.com/p/ff234438a87a

既然是要自定义注解处理器,自然少不了注解啦,首先创建一个java Module(butterknife_annotation

/**
 * @author Gentle Wen
 */

@Retention(RetentionPolicy.RUNTIME)//运行时注解
@Target(ElementType.FIELD)//作用域在成员变量
public @interface LsBindView {
    int value();//注解需要填入的值,就是你的view id
}

二、自定义注解处理器

下面就是处理注解的APT,首先我们创建一个java Module 并先引用一些依赖包,当然包括上面创建的注解的module

dependencies {
    //注解处理器需要用到的依赖,这里应用谷歌的
    implementation 'com.google.auto.service:auto-service:1.0-rc6'
    implementation project(':butterknife_annotation')
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
}
/**
 * 创建日期:2020/5/30 0007
 *
 * @author :Gentle Wen
 * describe:
 */
@AutoService(Processor.class)
public class ButterKnifeProcessor extends AbstractProcessor {
    /**
     * 注解处理器元素工具类
     */
    private Elements elementUtils;
    /**
     * 注解处理器操作相关的
     */
    private Filer filer;
    /**
     * 注解处理器打印相关
     */
    private Messager messager;
//    private Map javaCodeCreatorMap = new HashMap<>();

    /**
     * 注解处理器支持的注解
     *
     * @return
     */
    @Override
    public Set getSupportedAnnotationTypes() {
        Set set = new LinkedHashSet<>();
        set.add(LsBindView.class.getCanonicalName());
        return set;
    }

    /**
     * jdk版本号
     *
     * @return
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    /**
     * 初始化注解处理器的时候调用
     *
     * @param processingEnvironment
     */
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        //元素的工具
        elementUtils = processingEnvironment.getElementUtils();
        //用code String生成一个java文件调用的类对象
        filer = processingEnvironment.getFiler();
        //打印
        messager = processingEnvironment.getMessager();

    }

    /**
     * 注解处理器的入口
     *
     * @param set
     * @param roundEnvironment
     * @return
     */
    @Override
    public boolean process(Set set, RoundEnvironment roundEnvironment) {
        messager.printMessage(Diagnostic.Kind.NOTE,"process start....");
        try {
            JavaCodeCreator javaCodeCreator = null;
            //得到有这个LsBindView.class所有元素
            Set elements = roundEnvironment.getElementsAnnotatedWith(LsBindView.class);
            for (Element element : elements) {
                //成员变量的元素
                VariableElement variableElement = (VariableElement) element;
                //Class节点
                TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();
                //注解
                LsBindView annotation = variableElement.getAnnotation(LsBindView.class);
                //viewId
                int viewId = annotation.value();
                if (javaCodeCreator == null) {
                    //创建一个java代码生成的工具类
                    javaCodeCreator = new JavaCodeCreator(typeElement, elementUtils);
                }
                javaCodeCreator.saveVariableElement(viewId, variableElement);
            }
            if (javaCodeCreator != null) {
                //在工具类中拼接完我们需要的代码
                String code = javaCodeCreator.createCode();
                //打印
                messager.printMessage(Diagnostic.Kind.NOTE,code);
                //利用该对象创建出我们需要的java文件
                JavaFileObject sourceFile = filer.createSourceFile(javaCodeCreator.getFullClassName(), javaCodeCreator.getTypeElement());
                Writer writer = sourceFile.openWriter();
                writer.write(code);
                writer.flush();
                writer.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
            messager.printMessage(Diagnostic.Kind.ERROR,e.getMessage());
        }
        messager.printMessage(Diagnostic.Kind.NOTE,"process finish....");
        return false;
    }
}

我们知道,使用ButterKnife会帮我们生成我们需要的辅助类的java文件,那么我们就用上面的注解处理器帮我们生成一个java文件,现在自定义个工具类(JavaCodeCreator),让代码帮我们生成指定格式的代码。

/**
 * 创建日期:2020/5/30 0007
 *
 * @author :Gentle Wen
 * describe:
 */
 
public class JavaCodeCreator {
    private Map variableElements = new HashMap<>();
    private TypeElement typeElement;

    private String packageName;

    public JavaCodeCreator(TypeElement typeElement, Elements elementUtils) {
        this.typeElement = typeElement;
        this.packageName = elementUtils.getPackageOf(typeElement).getQualifiedName().toString();
    }

    /**
     * 创建代码
     *
     * @return
     */
    public String createCode() {
        StringBuilder stringBuilder = new StringBuilder();
        //创建package com.xx.xxx;
        stringBuilder.append("package ").append(packageName).append(";").append("\n");
         //创建import com.xx.xx.XxActivity;
        stringBuilder.append("import ").append(typeElement.getQualifiedName().toString()).append(";").append("\n");
        //创建public class XxActivity_LsViewBinding{还有方法}
        stringBuilder.append("public class ").append(typeElement.getSimpleName()).append("_LsViewBinding").append("{\n");
        stringBuilder.append(createMethod());
        stringBuilder.append("}\n");

        return stringBuilder.toString();
    }

    /**
     * 创建bind方法
     * 这里帮我们创建出我们需要的方法
     * @return
     */
    private String createMethod() {
        String activityStr = "activity";
        String findViewById = "findViewById";
        StringBuilder stringBuilder = new StringBuilder();
        //public void bind(com.xxx.xxx.XxActivity activity){
        stringBuilder.append("\t\tpublic void bind").append("(").append(typeElement.getQualifiedName()).append(" ").append(activityStr).append(")").append("{\n");

        for (Integer integer : variableElements.keySet()) {
            VariableElement variableElement = variableElements.get(integer);
            stringBuilder.append("\t\t\t\t").append(activityStr)
                    .append(".").append(variableElement.getSimpleName()).append(" = ")
                    .append("(").append(variableElement.asType().toString()).append(")")
                    .append(activityStr).append(".").append(findViewById).append("(").append(integer).append(")").append(";\n");

        }
        stringBuilder.append("\t\t}\n");
        return stringBuilder.toString();
    }

    public void saveVariableElement(int viewId, VariableElement variableElement) {
        variableElements.put(viewId, variableElement);
    }

    public String getFullClassName() {
        return typeElement.getQualifiedName().toString()+"_LsViewBinding";
    }

    public Element getTypeElement() {
        return typeElement;
    }
}

三、工具类

创建一个java Module (butterknife_utils),用于传入我们的activity,调用我们生成的XxActivity_LsViewBinding对象的方法

/**
 * 创建日期:2020/5/30  0007
 *
 * @author :Gentle Wen
 * describe:
 */
public class ButterKnifeManager {
    public static void inject(Activity activity){
        try {
            //你当前activity的对象的class
            Class activityClass = activity.getClass();
            //根据你当前对象的路径反射找到我们生成的XxActivity_LsViewBinding路径
            Class clazz = Class.forName(activityClass.getName()+"_LsViewBinding");
            //反射我们的bindView方法,形参就是我们的activity
            Method method = clazz.getMethod("bind", activityClass);
            //带入activity参数执行我们的bindView方法
            method.invoke(clazz.newInstance(),activity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

四、项目中使用

1.build.gradle

defaultConfig {
    javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
}   
implementation project(':butterknife_utils')
implementation project(':butterknife_annotation')
annotationProcessor project(':butterknife')

2.MainActivity

package com.xx.xx;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;

import com.xx.butterknife_annotation.LsBindView;
import com.xx.butterknife_utils.ButterKnifeManager;

public class MainActivity extends Activity {
    @LsBindView(R.id.button)
    Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnifeManager.inject(this);
        button.setOnClickListener(v -> {
            Log.i("MainActivity", "onClick: " + "LsBindView实现");
        });
    }

}

3.build一下你的project,之后就会在你的build文件夹路径下生成一个MainActivity_LsViewBinding的java文件

package com.xx.xx;
import com.xx.xx.MainActivity;
public class MainActivity_LsViewBinding{
      public void bind(com.xx.xx.MainActivity activity){
            activity.button = (android.widget.Button)activity.findViewById(2130968578);
      }
}

4.运行你的项目,点击按钮

MainActivity: onClick: LsBindView实现

总结

综上所述,我们可以知道,自定义注解处理器可以帮我们实现我们简单却又不得不实现的逻辑。在众多的框架中我们都可以看到注解处理器的影子(如:ButterKnife、Daggar等),能够掌握APT,也能在往Android架构师的路上添砖加瓦啊。

你可能感兴趣的:(Java注解知多少之自定义注解处理器)