介绍
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