手写实现Butterknife


介绍

Butterknife源码地址

https://github.com/JakeWharton/butterknife

引入

implementation 'com.jakewharton:butterknife:8.5.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'

主要是解决掉findViewById、setOnclick 、资源的注入等。反射造成大量的临时文件,造成gc,影响性能。而Butterknife采用编译时注解,即用APT mirror生成代码。

ButterKnife的工作原理:

声明的注解的生命周期为CLASS。继承AbstractProcessor类,再调用AbstractProcessor的process方法。

编译的时候扫描注解,调用javapoet库,生成java代码。

调用ButterKnife.bind(this);方法的时候,将ID与对应的上下文绑定在一起。

思路

发现编译后生成类名_ViewBinding,那么我们手动实现Butterknife,先写下伪代码:

新建类名_ViewBinding

public class ButterknifeActivity_ViewBinding {

    private ButterknifeActivity target;

    @UiThread
    public ButterknifeActivity_ViewBinding(ButterknifeActivity target) {
        this.target = target;
        target.textView = target.findViewById(R.id.tv);
    }

    @CallSuper
    public void unbind() {
        ButterknifeActivity target = this.target;
        if (target == null) throw new IllegalStateException("Bindings already cleared.");
        this.target = null;
        target.textView = null;
    }
}

在Activity中不需要再findViewById:

public class ButterknifeActivity extends AppCompatActivity {
    public TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_butterknife);

        ButterknifeActivity_ViewBinding viewBinding = new ButterknifeActivity_ViewBinding(this);
        textView.setText("我是伪代码");
    }
}

所以,我们的思路就是用APT实现伪代码,就实现了手写Butterknife。

实现

前奏

新建Module-Library

Android Library:butterknife
Java Library:butterknife-annotations(运行时的注解)和butterknife-compiler(编译,生成代码)

配置

app的build中引入

implementation project(':butterknife-annotations')
implementation project(':butterknife')
annotationProcessor project(':butterknife-compiler')

butterknife-compiler中引入

implementation 'com.google.auto.service:auto-service:1.0-rc3'//注解
implementation 'com.squareup:javapoet:1.8.0'//生成代码文件
implementation project(':butterknife-annotations')//依赖butterknife-annotations

注解处理器是(Annotation Processor)是javac的一个工具,用来在编译时扫描和编译和处理注解(Annotation)。在编译时把标记了注解的类,变量等作为输入内容,经过注解处理器处理,生成想要生成的java代码。处理器都是继承于AbstractProcessor

APT(Annotation Processing Tool)是一种处理注解的工具,它对源代码文件进行检测找出其中的Annotation,根据注解自动生成代码。

注解处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。

@AutoService(Processor.class)
public class ButterKnifeProcessor extends AbstractProcessor {

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        //先进init
    }

    //用来指定支持的 SourceVersion
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    //用来指定支持的 AnnotationTypes
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> types = new LinkedHashSet<>();
        for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {
            types.add(annotation.getCanonicalName());
        }
        return types;
    }

    //绑定哪些注解
    private Set<Class<? extends Annotation>> getSupportedAnnotations() {
        Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>();
        annotations.add(BindView.class);
        return annotations;
    }

    //编译时候执行,核心方法,有注解就都会进来这个方法,可以扫描,处理注解等,并且可以生成java代码。
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        return false;
    }
}

APT流程:编译开始 → 拿到元素(添加指定的注解元素) → 处理分解元素以及注解信息 → 通过JAVAPOET拼装生成Java文件 → 交给编译器。

AbstractProcessor中的init方法中的参数ProcessingEnvironment

public interface ProcessingEnvironment {
    Map<String, String> getOptions();
    Messager getMessager();
    Filer getFiler();
    Elements getElementUtils();
    Types getTypeUtils();
    SourceVersion getSourceVersion();
    Locale getLocale();
}

ProcessingEnvironment提供了一些工具类,Filer用于创建文件,Elements获取所有源文件元素,Types获取源代码类型信息。

反射

唯一使用反射的地方:

不在class中写入

Class_ViewBinding viewBinding = new Class_ViewBinding(this);

而是通过

ButterKnife.bind(this)//中对Class_ViewBinding进行有参构造反射

实现

生成的类在/build/generated/source/apt/base/包名/ClassName_ViewBinding类。

代码

https://github.com/AdamRight/TeaTool/tree/master/app/src/main/java/com/tea/teatool/teabutterknife
https://github.com/AdamRight/TeaTool

文章作者:
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 !
  目录