注解+APT使用

注解
注解单独存在是没有意义的,需要和其他连用比如:
  注解+APT 用于生成一些java文件,如: butterknife  dagger2  hilt  databinding
  注解+动态代理
  注解+代码埋点 如:AspectJ  Arouter
  注解+反射    如:xutils lifecycle

注解的作用:

1、降低项目的耦合度
2、自动完成一些规律性的代码
3、自动生成一些java代码,减轻开发者工作量

自定义注解的创建:

public @interface BindView {
      int value();
}

注:BindView是可以随意起的类名,注解在创建的时候选择Annotation创建,关键标识是@interface。

注解里成员的定义

1.当只有一个成员可以将其定义为value,使用时可以只写注解标识  @BindView
2.当有多个成员的时候需要在使用注解的时候@BindView(key = value,key2 = value2)形式。

元注解:可以修饰自定义注解

元注解常用的4中注解:@Target、@Retention、@Inherited、@Documented

@Target :表示注解类的作用对象

TYPE    // 类、接口、枚举类
FIELD     // 成员变量(包括:枚举常量)
METHOD    // 成员方法
PARAMETER     // 方法参数
CONSTRUCTOR     // 构造方法
LOCAL_VARIABLE     // 局部变量
ANNOTATION_TYPE    // 注解类
PACKAGE     // 可用于修饰包
TYPE_PARAMETER     // 类型参数,JDK 1.8 新增
TYPE_USE     //使用类型的任何地方,JDK 1.8新增

@Retention:表示注解的作用域

RetentionPolicy.RUNTIME // 注解保留到运行时
RetentionPolicy.CLASS // 注解保留到编译时
RetentionPolicy.SOURCE //注解保留到源码阶段(编译后.class文件不会有这个注解)

@Documented : javac的工具文档化,一般不关心

@Inherited:标明所修饰的注解,在所作用的类上,是否可以被继承。例:

//定一个Inherited注解的自定义注解
@Inherited
public @interface InheritAnnotation {
}

//使用这个自定义注解
@InheritAnnotation
public class Human {
}

//被注解修饰的类可以直接被其他类继承使用
class Man extends Human{

}

APT:(Annotation Processing Tool)注解处理工具,主要是生成一些java类

使用关键词:AbstractProcessor、@AutoService(Processor.class)

接下来用代码事例看是如何使用的:

1、首先在安卓工程中创建两个JAVA类型的module:

image

2、app的工程中依赖这两个java module:


implementation project(path:':apt-annotation')

annotationProcessor project(path:':apt-processor')

3、apt-processor依赖于apt-annotation,并在apt-processor的build.gradle中添加Google自动生成文件的第三方:


annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'

compileOnly 'com.google.auto.service:auto-service:1.0-rc4'

如图:

image

准备工作做好了!下面这个例子是仿ButterKnife


在apt-annotation中创建BindView注解类


/**

* Created by:zx

* on 9/7/21
* 自定义一个注解类

**/

@Target(ElementType.FIELD)//用在字段上
@Retention(RetentionPolicy.SOURCE)//源码范围
public @interface BindView {
    int value();
}


在apt-processor中创建一个类AnnotationCompiler并继承AbstractProcessor,且使用注解@AutoService(Processor.class) 目的是注册注解处理器。

package com.zx.apt_processor;

import com.google.auto.service.AutoService;
import com.zx.apt_annotation.BindView;

import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;

/**
 * Created by:zx
 * on 9/7/21
 * 这个注解处理器APT  用于自动生成java文件
 * 会在编译的时候运行
 **/
@AutoService(Processor.class)//注册注解处理器  注意:Processor这个别写错了
public class AnnotationCompiler extends AbstractProcessor {


    //支持的版本
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    //能够处理哪个注解
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new HashSet<>();
        types.add(BindView.class.getCanonicalName());
//        types.add(Override.class.getCanonicalName());
        return types;
        //这里要注意返回types
    }

    //定义一个用来生成APT目录下面的文件的对象
    Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        //初始化filer
        filer = processingEnvironment.getFiler();
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {

        processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "zx----------------"+set);

        //获取app中所有写了BindView注解的对象的集合
        //混合了所有的标注的类型:
        // TypeElement 类
        // ExecutableElement 方法
        // VariableElement 属性
        Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(BindView.class);

        Map<String, List<VariableElement>> map = new HashMap<>();
        for (Element element : elementsAnnotatedWith) {
            VariableElement variableElement = (VariableElement) element;
            String activityName = variableElement.getEnclosingElement().getSimpleName().toString();
            Class aClass = variableElement.getEnclosingElement().getClass();
            List<VariableElement> variableElements = map.get(activityName);
            if (variableElements == null) {//这里不要写错variableElements
                variableElements = new ArrayList<>();
                map.put(activityName, variableElements);
            }
            variableElements.add(variableElement);
        }


        if (map.size() > 0) {
            Writer writer = null;
            Iterator<String> iterator = map.keySet().iterator();
            while (iterator.hasNext()) {
                String activityName = iterator.next();
                List<VariableElement> variableElements = map.get(activityName);
                //得到包名
                TypeElement enclosingElement = (TypeElement) variableElements.get(0).getEnclosingElement();
                String packageName = processingEnv.getElementUtils().getPackageOf(enclosingElement).toString();
                try {
                    JavaFileObject sourceFile = filer.createSourceFile(packageName + "." + activityName + "_ViewBinding");
                    writer = sourceFile.openWriter();
                    //        package com.example.dn_butterknife;
                    writer.write("package " + packageName + ";\n");
                    //        import com.example.dn_butterknife.IBinder;
                    writer.write("import " + packageName + ".IBinder;\n");
                    //        public class MainActivity_ViewBinding implements IBinder<
                    //        com.example.dn_butterknife.MainActivity>{
                    writer.write("public class " + activityName + "_ViewBinding implements IBinder<" +
                            packageName + "." + activityName + ">{\n");
                    //            public void bind(com.example.dn_butterknife.MainActivity target) {
                    writer.write(" @Override\n" +
                            " public void bind(" + packageName + "." + activityName + " target){");
                    //target.tvText=(android.widget.TextView)target.findViewById(2131165325);
                    for (VariableElement variableElement : variableElements) {
                        //得到名字
                        String variableName = variableElement.getSimpleName().toString();
                        //得到ID
                        int id = variableElement.getAnnotation(BindView.class).value();
                        //得到类型
                        TypeMirror typeMirror = variableElement.asType();
                        writer.write("target." + variableName + "=(" + typeMirror + ")target.findViewById(" + id + ");\n");
                    }

                    writer.write("\n}}");

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (writer != null) {
                        try {
                            writer.close();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }

        return false;
    }
}


创建完成之后需要build一下工程,让它自动生成build里的文件。


在主工程app中创建IBinder一个接口

/**

* Created by:zx

* on 9/7/21

* 自定义一个接口用来绑定activity

**/

public interface IBinder {

    void bind(T target);

}

自定义一个MyButterknife类

/**

* Created by:zx

* on 9/8/21

**/

public class MyButterknife {

       public static void bind(Activity activity) {

            String name = activity.getClass().getName() +"_ViewBinding";

            try {

                Class aClass = Class.forName(name);

                IBinder iBinder = (IBinder) aClass.newInstance();

                iBinder.bind(activity);

                }catch (Exception e) {

                   e.printStackTrace();

               }

           }

}

解释 :
Class aClass = Class.forName(name);
IBinder iBinder = (IBinder) aClass.newInstance();
这两句的使用相当于是:IBinder IBinder= new IBinder();
但是区别在于:
    1.new出来的对象可以调用当前类里所有public方法,而newInstance()实例化出来的对象只能调用的是此类无参的构造方法;
    2.好处在于newInstance()这种形式需要分成两部,第一步是forName(“权限定名”)加载链接上,之后才能使用newInstance()实例化,有解耦的作用

在Activity中使用:

public class AnnotationActivityextends AppCompatActivity {

//使用自己定义的注解

    @BindView(R.id.test)

    TextView textView;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_annotation);

        //传入Activity用于绑定

        MyButterknife.bind(this);

        textView.setText("赋值成功");

        }

}

最后已经完成了注解+APT的结合,可以查看一下工程的app/build/generated/ap_generated_sources/debug/out/包名/自动生成的Activity的文件。


另外一个样例用于类似于枚举使用:


image.png

注:AS的版本是3.5.2,gradle版本是5.4.1。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容