ButterKnife源码解析
[TOC]
本文基于ButterKnife版本:
'com.jakewharton:butterknife:10.2.1'
'com.jakewharton:butterknife-compiler:10.2.1'
1-自定义注解处理器
java代码编译期,javac会调用java注解器来处理注解相关。先看下butterknife-compiler库中的核心ButterKnifeProcessor
通过继承AbstractProcessor实现自定义处理器,重写以下方法
public class ButterKnifeProcessor extends AbstractProcessor {
//指定java版本
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
//(1)初始化辅助类
@Override
public synchronized void init(ProcessingEnvironment env) {
super.init(env);
String sdk = env.getOptions().get(OPTION_SDK_INT);
if (sdk != null) {
try {
this.sdk = Integer.parseInt(sdk);
} catch (NumberFormatException e) {
env.getMessager()
.printMessage(Kind.WARNING, "Unable to parse supplied minSdk option '"
+ sdk
+ "'. Falling back to API 1 support.");
}
debuggable = !"false".equals(env.getOptions().get(OPTION_DEBUGGABLE));
typeUtils = env.getTypeUtils();
filer = env.getFiler();
try {
trees = Trees.instance(processingEnv);
} catch (IllegalArgumentException ignored) {
try {
// Get original ProcessingEnvironment from Gradle-wrapped one or KAPT-wrapped one.
for (Field field : processingEnv.getClass().getDeclaredFields()) {
if (field.getName().equals("delegate") || field.getName().equals("processingEnv")) {
field.setAccessible(true);
ProcessingEnvironment javacEnv = (ProcessingEnvironment) field.get(processingEnv);
trees = Trees.instance(javacEnv);
break;
}
}
} catch (Throwable ignored2) {
}
}
}
//返回目标注解集合
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new LinkedHashSet<>();
for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {
types.add(annotation.getCanonicalName());
}
return types;
}
//(2)核心方法。先扫描查找注解,再生成java文件
@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
//@1.查找注解信息,生成BindingSet并保存到bingdingMap中
Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);
//@4.遍历bindingMap,生成对应的 className_ViewBinding.java文件
for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
TypeElement typeElement = entry.getKey();
BindingSet binding = entry.getValue();
JavaFile javaFile = binding.brewJava(sdk, debuggable);
try {
javaFile.writeTo(filer);
} catch (IOException e) {
error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
}
}
return false;
}
}
(1)初始相关辅助类init()
init方法中涉及的几个辅助类\变量
(2)核心操作process()方法
@1.扫描查找注解
private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();
Set<TypeElement> erasedTargetNames = new LinkedHashSet<>();
// @BindView 注解
for (Element element : env.getElementsAnnotatedWith(BindView.class)) {
try {
//@2.处理BindView的注解
parseBindView(element, builderMap, erasedTargetNames);
} catch (Exception e) {
logParsingError(element, BindView.class, e);
}
}
//省略@BindViews @BindString等注解处理
。。。
//@3.监听处理,OnClick,OnItemClick等
for (Class<? extends Annotation> listener : LISTENERS) {
findAndParseListener(env, listener, targetClassMap, erasedTargetNames);
}
for (Map.Entry<TypeElement, BindingClass> entry : targetClassMap.entrySet()) {
TypeElement parentType = findParentType(entry.getKey(), erasedTargetNames);
if (parentType != null) {
BindingClass bindingClass = entry.getValue();
BindingClass parentBindingClass = targetClassMap.get(parentType);
bindingClass.setParent(parentBindingClass);
}
}
}
@2.处理@BindView。这里涉及到BindingSet辅助类,缓存相关注解信息。ViewBiding
private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap,
Set<TypeElement> erasedTargetNames) {
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
// 检测注解语法是否合法。注解方法不能为private、static,注解类不能是android/java包
// 若非法,抛出异常
boolean hasError = isInaccessibleViaGeneratedCode(BindView.class, "fields", element)
|| isBindingInWrongPackage(BindView.class, element);
// Verify that the target type extends from View.
TypeMirror elementType = element.asType();
if (elementType.getKind() == TypeKind.TYPEVAR) {
TypeVariable typeVariable = (TypeVariable) elementType;
elementType = typeVariable.getUpperBound();
}
Name qualifiedName = enclosingElement.getQualifiedName();
Name simpleName = element.getSimpleName();
if (!isSubtypeOfType(elementType, VIEW_TYPE) && !isInterface(elementType)) {
if (elementType.getKind() == TypeKind.ERROR) {
note(element, "@%s field with unresolved type (%s) "
+ "must elsewhere be generated as a View or interface. (%s.%s)",
BindView.class.getSimpleName(), elementType, qualifiedName, simpleName);
} else {
error(element, "@%s fields must extend from View or be an interface. (%s.%s)",
BindView.class.getSimpleName(), qualifiedName, simpleName);
hasError = true;
}
}
//不合法,返回
if (hasError) {
return;
}
// 获取注解中的对应的viewId
int id = element.getAnnotation(BindView.class).value();
BindingSet.Builder builder = builderMap.get(enclosingElement);
Id resourceId = elementToId(element, BindView.class, id);
if (builder != null) {
//判断ID是否已经绑定过,如果已绑定则抛出异常
String existingBindingName = builder.findExistingBindingName(resourceId);
if (existingBindingName != null) {
error(element, "Attempt to use @%s for an already bound ID %d on '%s'. (%s.%s)",
BindView.class.getSimpleName(), id, existingBindingName,
enclosingElement.getQualifiedName(), element.getSimpleName());
return;
}
} else {
//未绑定过,则创建BindingSet.Builder
builder = getOrCreateBindingBuilder(builderMap, enclosingElement);
}
String name = simpleName.toString();
TypeName type = TypeName.get(elementType);
boolean required = isFieldRequired(element);
//将当前注解生成FieldViewBinding并添加到builder中
builder.addField(resourceId, new FieldViewBinding(name, type, required));
// Add the type-erased version to the valid binding targets set.
erasedTargetNames.add(enclosingElement);
}
@3.监听处理,OnClick,OnItemClick等
private void findAndParseListener(RoundEnvironment env,
Class<? extends Annotation> annotationClass,
Map<TypeElement, BindingSet.Builder> builderMap, Set<TypeElement> erasedTargetNames) {
for (Element element : env.getElementsAnnotatedWith(annotationClass)) {
if (!SuperficialValidation.validateElement(element)) continue;
try {
//核心方法,解析注解
//并将listener注解对应的信息封装到MethodViewBinding中
//将MethodViewBinding添加到builder中
parseListenerAnnotation(annotationClass, element, builderMap, erasedTargetNames);
} catch (Exception e) {
StringWriter stackTrace = new StringWriter();
e.printStackTrace(new PrintWriter(stackTrace));
error(element, "Unable to generate view binder for @%s.\n\n%s",
annotationClass.getSimpleName(), stackTrace.toString());
}
}
}
(3)注册自定义注解器。通过google提供的auto-service库来通过注解注册
compile 'com.google.auto.service:auto-service:1.0-rc2
@AutoService(Processor.class)
public class ButterKnifeProcessor extends AbstractProcessor {
...
}
2-生成对应的className_ViewBinding.java
@4.遍历bindingMap,生成对应的 className_ViewBinding.java文件。例如业务使用
//ActivityB.java
public class ActivityB extends BaseActivity{
@BindView(R.id.edit_txt)
EditText editText;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_b);
//@1.绑定
ButterKnife.bind(this);
}
@OnClick(R.id.btn_one)
protected void postMsg() {
String msg = editText.getText().toString();
EventBus.getDefault().post(MessageWrap.getInstance(msg));
this.finish();
}
}
经过安装编译后在指定目录下生成了ActivityB_ViewBinding.java。对应的方法都是指定在UI线程执行,且通过持有目标引用直接调用对应的属性或方法,所以要求注解的变量或方法必须为非private、static
public class ActivityB_ViewBinding implements Unbinder {
private ActivityB target;//目标类的引用
private View view7f070044;//与click事件绑定的view
@UiThread
public ActivityB_ViewBinding(ActivityB target) {
this(target, target.getWindow().getDecorView());
}
@UiThread
public ActivityB_ViewBinding(final ActivityB target, View source) {
this.target = target;
//实现findViewById及setOnClickListener
View view;
target.editText = Utils.findRequiredViewAsType(source, R.id.edit_txt, "field 'editText'", EditText.class);
view = Utils.findRequiredView(source, R.id.btn_one, "method 'postMsg'");
view7f070044 = view;
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
target.postMsg();
}
});
}
//解绑
@Override
@CallSuper
public void unbind() {
ActivityB target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
this.target = null;
target.editText = null;
view7f070044.setOnClickListener(null);
view7f070044 = null;
}
}
使用原理,在ActivityB.setContentView之后调用ButterKnife.bind(this)来完成绑定。-->ButterKnife.bind(target, sourceView)。target即为ActivityB的实例,sourceView为其DecorView
@NonNull @UiThread
public static Unbinder bind(@NonNull Object target, @NonNull View source) {
//通过ActivityB的类名拼接获取到对应的ActivityB_ViewBinding.java类名
Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
//@5.获取对应ViewBinding类的构造器
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
//创建对应的ViewBinding类实例
//即在ViewBinding初始化时完成了ActivityB的
//findViewById及setOnClickListener等操作
return constructor.newInstance(target, source);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InstantiationException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException("Unable to create binding instance.", cause);
}
}
@5.获取对应ViewBinding类的构造器
@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
//从缓存中读取对应class的构造器
if (bindingCtor != null || BINDINGS.containsKey(cls)) {
if (debug) Log.d(TAG, "HIT: Cached in binding map.");
return bindingCtor;
}
//java\android\androidx包下的类不处理
String clsName = cls.getName();
if (clsName.startsWith("android.") || clsName.startsWith("java.")
|| clsName.startsWith("androidx.")) {
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return null;
}
try {
//获取ClassLoader并加载对应的ViewBinding类
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
//通过反射获取ViewBinding的Class对象的Constructor即构造器
bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
if (debug) Log.d(TAG, "HIT: Loaded binding class and constructor.");
} catch (ClassNotFoundException e) {
if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());
bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
} catch (NoSuchMethodException e) {
throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
}
//缓存构造器
BINDINGS.put(cls, bindingCtor);
return bindingCtor;
}
3-流程总结
- (1)自定义AnnotationProcessor,ButterKnifeProcessor并注册
- (2).java-->.class的过程中,通过ButterKnifeProcessor生成ViewBinding类文件
- 处理@BindView@BindViews等注解,根据注解类型封装相关信息到ViewBinding、ResourceBinding等
- 处理@OnClick@OnItemClick等Listener注解,将相关注解信息及对应的方法信息封装到MethodViewBinding
- 这些注解信息都在class对应的注解辅助类BindingSet中,以绑定类的TypeElement为key,类中所有注解信息BindingSet为value,添加到bindingMap中
- 遍历bindingMap,通过BindingSet相关信息生成绑定类的对应的ViewBinding类文件。
- (3)Activity中调用ButterKnife.bind(this)
- (4)反射获取编译期生成的Activity_ViewBinding文件,通过ClassLoader加载,反射获取其构造方法并调用
- (5)ViewBinding类的构造方法中持有目标类的引用,调用目标类的findViewbyId、setOnClickListener等操作完成绑定。