ButterKnife源码阅读以及手写自定义注解框架

ButterKnife是一款通过注解,绑定控件的id,String,Bitmap等资源id的注解框架
同时ButterKnife是一个编译时框架,所以对代码性能影响几乎为0
使用注解之前:

首先要新建java的library,然后注册注解(添加注解的依赖),告诉jvm我们这个module里面自定义注解处理器

ButterKnife源码阅读以及手写自定义注解框架_第1张图片

   implementation 'com.google.auto.service:auto-service:1.0-rc3'
    //如果AS版本是3.4以上  gradle 5.1.1-all 使用以下依赖
    //annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
    //compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
    implementation project(':annotation')
我们新建两个java的lib

一个叫做annotation,另一个叫做annotation_complier,如图添加依赖

ButterKnife源码阅读以及手写自定义注解框架_第2张图片

拿BindView功能来说,ButterKnife有个BindView的接口
我们在annotation下面新建一个类叫做BindView
@Target(ElementType.FIELD)//申明注解的类型,是放在什么上面的
@Retention(RetentionPolicy.SOURCE)//声明注解的生命周期 java-class-run  编译期CLASS   运行期RUNTIME 源码期SOURCE,
public @interface BindView {
    int value();
}
在annotation_complier新建一个类叫做AnnotationCompilier
package com.dhd.annotation_complier;

import com.dhd.annotation.BindView;
import com.google.auto.service.AutoService;

import java.io.IOException;
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.JavaFileObject;

/**
 * 注解处理器,生成activity相对应的类
 */
@AutoService(Processor.class)//注册注解器
public class AnnotationCompilier extends AbstractProcessor {
    //生成文件对象
    Filer mFiler;

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

    /**
     * @return 申明注解处理器要处理的注解
     */
    @Override
    public Set getSupportedAnnotationTypes() {
        Set types = new HashSet<>();
        types.add(BindView.class.getCanonicalName());//把要处理的注解添加到集合
        return types;
    }

    /**
     * @return 申明当前注解处理器支持的版本
     */
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return processingEnv.getSourceVersion();
    }

    /**
     * 写文件(acitivy或者frag的注解类)
     *
     * @param set
     * @param roundEnvironment
     * @return
     */
    @Override
    public boolean process(Set set, RoundEnvironment roundEnvironment) {
        //拿到整个模块中使用到Bindview的节点
        Set elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(BindView.class);
        Map> map = new HashMap<>();
        for (Element element : elementsAnnotatedWith) {
            //获取成员变量的节点(控件)
            VariableElement variableElement = (VariableElement) element;
            String activityName = variableElement.getEnclosingElement().getSimpleName().toString();
            List variableElements = map.get(activityName);
            if (variableElements == null) {
                variableElements = new ArrayList<>();
                map.put(activityName, variableElements);
            }
            variableElements.add(variableElement);
        }
        if (map.size() > 0) {
            Writer writer = null;
            Iterator iterator = map.keySet().iterator();
            while (iterator.hasNext()) {
                String activityName = iterator.next();
                List variableElements = map.get(activityName);
                //通过成员变量获取上一个节点的类节点
                Element enclosingElement = variableElements.get(0).getEnclosingElement();
                //通过成员变量得到包名
                String packageName = processingEnv.getElementUtils().getPackageOf(enclosingElement).toString();
                try {
                    JavaFileObject sourceFile = mFiler.createSourceFile(packageName + "." + activityName + "_ViewBinding");
                    writer = sourceFile.openWriter();
                    writer.write("package " + packageName + ";\n");
                    writer.write("import " + packageName + ".IBinder;\n");
                    writer.write("public class " + activityName + "_ViewBinding implements IBinder<" + packageName + "." + activityName + ">{\n\n");
                    writer.write("      @Override\n" + "      public void bind(" + packageName + "." + activityName + " target) {\n");
                    for (VariableElement variableElement : variableElements) {
                        //获取控件的名字
                        String variableName = variableElement.getSimpleName().toString();
                        //获取控件ID
                        int id = variableElement.getAnnotation(BindView.class).value();
                        //获取控件的类型
                        TypeMirror typeMirror = variableElement.asType();
                        writer.write("      targer." + variableName + "=(" + typeMirror + ")target.findViewById(" + id + ");\n");
                    }
                    writer.write("  }\n}\n");

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (writer != null) {
                        try {
                            writer.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return false;
    }
}
自定义注解需要继承AbstractProcessor类,并且申明@AutoService(Processor.class)为Processor.class
在实现类里面需要复写几个方法
第一个是初始化Filer的init方法
第二个是复写支持的资源版本getSupportedSourceVersion方法
第三个是getSupportedAnnotationTypes方法,该方法返回支持的注解类型集合(比如BindView,BindString,BingBitmap等)
最后一个也是我们处理很多业务逻辑的以及写入文件的方法process方法,这个方法我们可以拿到调用自定义注解的类名,类里面的方法,控件等等
然后通过Writer写入文件,该java文件实现初始化接口的方法,在初始化方法比如bind中去findViewById(),写入的代码会在编译时生成对应的java文件;
然后在activity中调用的时候需要提供一个初始化的方法,比如叫ButterKnife.bind(this),
那这个bind方法通过反射的方式拿到初始化方法接口的实例,传入调用者的上下文this,即可实现绑定
public class ButterKnife {
    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();
        }
    }
}


public interface IBinder {
    void bind(T target);
}

最后在MainActivity中调用

public class MainActivity extends AppCompatActivity {
    @BindView(R.id.textview)
    TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        mTextView.setText("自定义注解");
    }
}

整体来说主要是annotationProcessor的api使用,如果要实现BindString,BindBitmap,代码也是类似的

 

你可能感兴趣的:(android)