项目创建
创建一个新项目,然后创建三个子Module
- Android Library:
butterknife
,里面包含了butterknife的一些简易的模板类 - Java Library:
butterknife-annotations
,包含了butterknife所需的注解 - Java Library:
butterknife-compiler
,butterknife的注解处理器
1.在butterknife-compiler
Module中导入所需的依赖
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
// 注解Module
implementation project(path: ':butterknife_annotations')
// 用于Java代码生成
api 'com.squareup:javapoet:1.11.1'
// 注解处理器,gradle5.0之后需要这样(原因不清楚~)
api 'com.google.auto.service:auto-service:1.0-rc6'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
}
//中文乱码问题(错误:编码GBK不可映射字符)
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
sourceCompatibility = "8"
targetCompatibility = "8"
2.在butterknife-annotations
Module中定义注解BindView
package com.butterknife.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface BindView {
int value();
}
3.在butterknife工程中定义简易的几个类
package com.butterknife;
import androidx.annotation.UiThread;
/**
* 用于解绑Activity引用
* Created by FangJu on 2020/4/1.
*/
public interface Unbinder {
@UiThread
void unbind();
Unbinder EMPTY = new Unbinder() {
@Override
public void unbind() {
}
};
}
package com.butterknife;
import android.app.Activity;
import android.view.View;
/**
* Created by FangJu on 2020/4/1.
*/
public class ButterKnifeUtil {
public static <T extends View> T findViewById(Activity activity, int viewId) {
return activity.findViewById(viewId);
}
}
package com.butterknife;
import android.app.Activity;
import java.lang.reflect.Constructor;
/**
* Created by FangJu on 2020/4/1.
*/
public class ButterKnife {
public static Unbinder bind(Activity activity) {
try {
Class<? extends Unbinder> className = (Class<? extends Unbinder>) Class.forName(activity.getClass().getName() + "_ViewBinding");
Constructor<? extends Unbinder> declaredConstructor = className.getDeclaredConstructor(activity.getClass());
declaredConstructor.setAccessible(true);
return declaredConstructor.newInstance(activity);
} catch (Exception e) {
e.printStackTrace();
}
return Unbinder.EMPTY;
}
}
4.在butterknife-compiler
中定义注解处理器
package com.butterknife.compiler;
import com.butterknife.annotations.BindView;
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.TypeSpec;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
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.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
@AutoService(Processor.class)
public class ButterKnifeProcessor extends AbstractProcessor {
private Filer filer;
private Elements elementUtils;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();
elementUtils = processingEnv.getElementUtils();
}
// 处理器可以处理的Java版本
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_8;
}
// 表示处理器可以处理哪些注解
@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;
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// 获取所有注解了BindView的类
Set<? extends Element> bindViewElements = roundEnv.getElementsAnnotatedWith(BindView.class);
// 定义一个map,key为类,value为类中的注解变量
// com.fangju.butterknife.LoginActivity---[tv1, tv2, tv3]
Map<Element, List<Element>> elementMap = new LinkedHashMap<>();
for (Element bindViewElement : bindViewElements) {
Element enclosingElement = bindViewElement.getEnclosingElement();
List<Element> elementList = elementMap.get(enclosingElement);
if (elementList == null) {
elementList = new ArrayList<>();
elementMap.put(enclosingElement, elementList);
}
elementList.add(bindViewElement);
}
// 生成代码
for (Map.Entry<Element, List<Element>> entry : elementMap.entrySet()) {
Element key = entry.getKey();
List<Element> value = entry.getValue();
System.out.println(key.toString() + "---" + value.toString());
// 构造类
String srcClassName = key.getSimpleName().toString();
ClassName superClassName = ClassName.get("com.butterknife", "Unbinder");
TypeSpec.Builder classBuilder =
TypeSpec.classBuilder(srcClassName + "_ViewBinding")
.addModifiers(Modifier.FINAL, Modifier.PUBLIC)
.addSuperinterface(superClassName);
// 加入字段
ClassName activityClassName = ClassName.bestGuess(srcClassName);
classBuilder.addField(activityClassName, "target", Modifier.PRIVATE);
// 实现方法
ClassName callSuperName = ClassName.get("androidx.annotation", "CallSuper");
MethodSpec.Builder unbindMethodBuilder =
MethodSpec.methodBuilder("unbind")
.addAnnotation(Override.class)
.addAnnotation(callSuperName)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
unbindMethodBuilder.addStatement("$T target = this.target", activityClassName);
unbindMethodBuilder.addStatement("if(target == null) throw new IllegalStateException(\"Bindingd already clear\")");
unbindMethodBuilder.addStatement("this.target = null");
for (Element element : value) {
String fileName = element.getSimpleName().toString();
unbindMethodBuilder.addStatement("this.target.$L = null", fileName);
}
// 将方法加入类中
classBuilder.addMethod(unbindMethodBuilder.build());
// 加入构造函数
MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
.addParameter(activityClassName, "target");
constructorBuilder.addStatement("this.target=target");
for (Element element : value) {
String fieldName = element.getSimpleName().toString();
ClassName utilName = ClassName.get("com.butterknife", "ButterKnifeUtil");
int resId = element.getAnnotation(BindView.class).value();
// target.tv1 = ButterKnifeUtil.findViewById(source, R.id.tv1)
constructorBuilder.addStatement("target.$L = $T.findViewById(target, $L)", fieldName, utilName, resId);
}
classBuilder.addMethod(constructorBuilder.build());
// 生成类
String pkgName = elementUtils.getPackageOf(key).getQualifiedName().toString();
try {
JavaFile.builder(pkgName, classBuilder.build())
.addFileComment("butterknife自动生成")
.build()
.writeTo(filer);
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
}
5.测试
package com.fangju.butterknife;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.butterknife.ButterKnife;
import com.butterknife.Unbinder;
import com.butterknife.annotations.BindView;
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv1)
TextView tv1;
@BindView(R.id.tv2)
TextView tv2;
@BindView(R.id.tv3)
TextView tv3;
private Unbinder unbinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
unbinder = ButterKnife.bind(this);
tv1.setText("测试");
}
@Override
protected void onDestroy() {
super.onDestroy();
unbinder.unbind();
}
}