前言
总结一下仿写的ButterKnife,以及所遇到的坑.
第一步 创建模块
创建两个Java Library 和一个Android Library
Java Library的一个是
butterknife-annotations —— 用来声明注解,
butterknife-compiler——用来自定义注解
Android Library
butterknife
第二步 导入依赖
导入依赖
butterknife-compiler中
.
annotationProcessor ‘com.google.auto.service:auto-service:1.0-rc6’
implementation ‘com.google.auto:auto-common:0.10’
implementation ‘com.squareup:javapoet:1.10.0’
implementation ‘com.google.auto.service:auto-service:1.0-rc6’
implementation project(path: ‘:butterknife-annotations’)
app中
.
implementation project(’:butterknife-annotations’)
annotationProcessor project(’:butterknife-compiler’)
implementation project(’:butterknife’)
第三步 编写代码
butterknife-annotations中
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface BindView {
int value();
}
butterknife-compiler中
@AutoService(Processor.class)
public class ButterKnifeProcessor extends AbstractProcessor {
private Filer mFiler;
private Elements mElementUtils;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mElementUtils = processingEnvironment.getElementUtils();
mFiler = processingEnvironment.getFiler();
}
//1。指定处理的版本
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
//2.需要处理的注解
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new LinkedHashSet<>();
for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {
types.add(annotation.getCanonicalName());
}
return types;
}
//2.声明需要的注解
private Set<Class<? extends Annotation>> getSupportedAnnotations() {
Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>();
//需要解析的自定义注解BindView OnClick
annotations.add(BindView.class);
return annotations;
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
//解析属性 activity -> List<Element>
Map<Element, List<Element>> elementsMap = new LinkedHashMap<>();
for (Element element : elements) {
Element enclosingElement = element.getEnclosingElement();
List<Element> viewBindElements = elementsMap.get(enclosingElement);
if (viewBindElements == null) {
viewBindElements = new ArrayList<>();
elementsMap.put(enclosingElement, viewBindElements);
}
viewBindElements.add(element);
}
//生成代码
for (Map.Entry<Element, List<Element>> entry : elementsMap.entrySet()) {
Element enclosingElement = entry.getKey();
List<Element> viewBindElements = entry.getValue();
// public final class xxxActivity_ViewBinding implements Unbinder
String activityClassNameStr = enclosingElement.getSimpleName().toString();
ClassName activityClassName = ClassName.bestGuess(activityClassNameStr);
ClassName unbinderClassName = ClassName.get("com.example.butterknife", "Unbinder");
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(activityClassNameStr + "_ViewBinding")
.addModifiers(Modifier.FINAL, Modifier.PUBLIC)
.addSuperinterface(unbinderClassName)
.addField(activityClassName, "target", Modifier.PRIVATE);
// 实现 unbind 方法
// android.support.annotation.CallSuper
ClassName callSuperClassName = ClassName.get("androidx.annotation","CallSuper");
MethodSpec.Builder unbindMethodBuilder = MethodSpec.methodBuilder("unbind")
.addAnnotation(Override.class)
.addAnnotation(callSuperClassName)
.addModifiers(Modifier.PUBLIC,Modifier.FINAL);
unbindMethodBuilder.addStatement("$T target = this.target",activityClassName);
unbindMethodBuilder.addStatement("if (target == null) throw new IllegalStateException(\"Bindings already cleared.\")");
// 构造函数
MethodSpec.Builder constructorMethodBuilder = MethodSpec.constructorBuilder()
.addParameter(activityClassName, "target");
constructorMethodBuilder.addStatement("this.target = target");
// findViewById 属性
for (Element viewBindElement : viewBindElements) {
// target.textView1 = Utils.findRequiredViewAsType(source, R.id.tv1, "field 'textView1'", TextView.class);
// target.textView1 = Utils.findViewById(source, R.id.tv1);
String filedName = viewBindElement.getSimpleName().toString();
ClassName utilsClassName = ClassName.get("com.example.butterknife","Utils");
int resId = viewBindElement.getAnnotation(BindView.class).value();
constructorMethodBuilder.addStatement("target.$L = $T.findViewById(target, $L)",filedName,utilsClassName,resId);
// target.textView1 = null;
unbindMethodBuilder.addStatement("target.$L = null",filedName);
}
classBuilder.addMethod(constructorMethodBuilder.build());
classBuilder.addMethod(unbindMethodBuilder.build());
try {
String packageName = mElementUtils.getPackageOf(enclosingElement).getQualifiedName().toString();
JavaFile.builder(packageName,classBuilder.build())
.addFileComment("butterknife 自动生成")
.build().writeTo(mFiler);
System.out.println("success!");
} catch (IOException e) {
System.out.println("翻车了!");
e.printStackTrace();
}
}
return false;
}
}
butterknife中有三个类
public class ButterKnife {
public static Unbinder bind(Activity activity) {
try {
Class<? extends Unbinder> bindClassName = (Class<? extends Unbinder>) Class.forName(activity.getClass().getName() + "_ViewBinding");
//构造函数
Constructor<? extends Unbinder> bindConstructor = bindClassName.getDeclaredConstructor(activity.getClass());
Unbinder unbinder = bindConstructor.newInstance(activity);
return unbinder;
} catch (Exception e) {
e.printStackTrace();
}
return Unbinder.EMPTY;
}
}
public interface Unbinder {
@UiThread
void unbind();
Unbinder EMPTY = new Unbinder() {
@Override
public void unbind() {
}
};
}
public class Utils {
public static <T extends View> T findViewById(Activity activity,int viewId){
return activity.findViewById(viewId);
}
}
app模块中
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv1)
TextView tv1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
}
以上就是用到的所有代码。
第四步 自动生成的结果
下面代码就是编译后自动生成的目录在
app\build\generated\ap_generated_sources\debug\out\包名
// butterknife 自动生成
public final class MainActivity_ViewBinding implements Unbinder {
private MainActivity target;
MainActivity_ViewBinding(MainActivity target) {
this.target = target;
target.tv1 = Utils.findViewById(target, 2131165359);
}
@Override
@CallSuper
public final void unbind() {
MainActivity target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
target.tv1 = null;
}
}
最后总结下遇到的坑
第一个就是这个方法没有调用
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {}
解决方法
把所有需要的依赖到进去即可
第二个是编译以后没有没有生成预期效果
解决方法
在app的类中做些修改再重新编译即可