学习Apt(日志) 一

开头该怎么说呢,我这个主要是记录下自己学习的日志,所以开场白也不知道怎么说才好。

那就这样说好了,参考网上诸多代码,然后自己模仿下 ButterKnife 的@BindView注解写法

1.创建一个新的项目

2.创建一个 放 自定义注解 java module

3.创建一个 放 注解处理器的 java module



4.在 annotation 项目中  创建一个自定义的注解  取名为 BindView



package com.example;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;



@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.FIELD)
public @interface BindView{
    int value() default -1;
}

5 compile 项目 添加3个依赖,并且创建一个注解处理器 取名为MyAnnotationProcessor,其中最后一个依赖为 我们自定义注解的项目。

注解处理器 处理 自定义的注解,没毛病吧。


package com.example;

import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarFile;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
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.Modifier;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.Elements;
import javax.tools.Diagnostic;

@AutoService(Processor.class)
public class MyAnnotationProcessor  extends AbstractProcessor{
    private Filer mFiler;
    private Messager mMessager;
    private Elements mElementUtils;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        mFiler = processingEnvironment.getFiler();
        mMessager = processingEnvironment.getMessager();
        mElementUtils = processingEnvironment.getElementUtils();
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotations = new LinkedHashSet<>();
        annotations.add(BindView.class.getCanonicalName());
        return annotations;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        Set<? extends Element> bindViewElements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
        note("bindViewElements.size:"+bindViewElements.size());
        if(bindViewElements==null || bindViewElements.size() == 0){
            //这里说一下返回的意义,如果返回false 表示 还有其他的注解处理器 也可以处理这个注解 true表示 其他的注解处理器 不需要再次处理了
            return false;
        }

        //先分类,比如这些 @BindView注解  是在哪些类中使用的 有可能在 AxxxActivty,BxxxActivty,CxxxActivty
        //到时候会生成辅助类  BindAxxxActivty,BindBxxxActivty,BindCxxxActivty,这个辅助类的名字是我们自己随便定义的
        Map<TypeElement,Set<Element>> groupMap  =new HashMap<>();//这个是用来存放 每个类,对应的所有@BindView注解
        for (Element element : bindViewElements){
            TypeElement  enclosingElement = (TypeElement) element.getEnclosingElement();
            Set<Element> elements  = groupMap.get(enclosingElement);
            if(elements==null){
                elements = new HashSet<>();
                groupMap.put(enclosingElement,elements);
            }
            elements.add(element);
        }

        //分好组之后,就是生成具体辅助类的代码了
        for (TypeElement typeElement : groupMap.keySet()){
            // 1.获取使用注解的地方 所在的包名  com.xxx.xxx.
            PackageElement packageElement = mElementUtils.getPackageOf(typeElement);
            String  pkName = packageElement.getQualifiedName().toString();
            note("所在的包名:"+pkName);

            //2.获取包装类类型,即获取使用注解所在类的全类名, com.xxx.xxx.MyClass
            String  enclosingName = typeElement.getQualifiedName().toString();
            note("所在的类全限定名:"+enclosingName);


            //添加辅助类的方法
            MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder("bindView")
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                    .addParameter(ClassName.get(typeElement.asType()),"activity")
                    .returns(TypeName.VOID);


            Set<Element> elements = groupMap.get(typeElement);
            for (Element element : elements){
                //因为BindView只作用于filed,所以这里可直接进行强转
                VariableElement bindViewElement = (VariableElement) element;

                //3.获取注解的成员变量名
                String bindViewFiledName = bindViewElement.getSimpleName().toString();
                note("变量名:"+bindViewFiledName);
                //4.获取注解的成员变量类型
                String bindViewFiledClassType = bindViewElement.asType().toString();
                note("变量名类型:"+bindViewFiledClassType);
                //5.获取注解元数据,就是注解的值 比如:@BindView(R.id.test) 中的 R.id.test
                BindView bindView = element.getAnnotation(BindView.class);
                int id = -1;
                if(bindView!=null){
                    id = bindView.value();
                }
                note("注解的值:"+id);
                //主要是这句,往方法里面添加代码
                methodSpecBuilder .addStatement(String.format("activity.%s = (%s)activity.findViewById(%d)",bindViewFiledName,bindViewFiledClassType,id));

            }
            TypeSpec typeSpec = TypeSpec.classBuilder("Bind"+typeElement.getSimpleName().toString())
                    //.superclass(TypeName.get(typeElement.getSuperclass()))
                    .addModifiers(Modifier.FINAL,Modifier.PUBLIC)
                    .addMethod(methodSpecBuilder.build())
                    .build();
            JavaFile file = JavaFile.builder(pkName,typeSpec).build();
            try {
                file.writeTo(processingEnv.getFiler());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    /**
     * 在Gradle Console 控制台可以看到日志
     * @param msg
     */
    private void note(String msg) {
        mMessager.printMessage(Diagnostic.Kind.NOTE, msg);
    }
}

5.弄完java module 后 ,再来看看 如何使用, 在app配置文件 添加一些依赖



6.依赖添加完后 就正常 编写 布局文件 然后在 Activity使用,当在多个Activity使用@BindView注解的时候,会产生多个辅助类文件



下面这些就是编译期产生的辅助类文件 跟原文件比就多了个 Bind前缀,这个我们可以自己随便定义的


看到黑色箭头没有,黑色箭头的代码不一样。黑色箭头 指向的类就是编译期 产生的辅助文件,这里我只有2个activity使用的@BindView注解,所以产生了BindMainActivity类和 BindLoginActivtiy类,并且在调用的时候 得使用对应的辅助类才行,比如 MainActivity 就得调用BindMainActivty.binView,LoginActivtiy就的 调用 BindLoginActivtiy.bindView 。如果有很N个类 使用@BindView注解的话 就得 使用N个辅助类对应的 bingView方法了,这个不太好,下一篇日志我会改造下

测试的结果:



已经赋值上去了,成功~~~~~~~~~·

学习Apt(日志) 二

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值