项目结构
butterknife-runtime
butterknife
butterknife-annotations
butterknife-compiler
butterknife-gradle-plugin
//以下是辅助
butterknife-integration-test
butterknife-lint
butterknife-reflect
项目依赖图:

如何使用:
1.先在项目根路径 build.gradle 里添加
classpath 'com.jakewharton:butterknife-gradle-plugin:10.1.0'
2.在app module build.gradle 里添加
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.jakewharton:butterknife:10.1.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.1.0'
implementation 'com.google.android:support-v4:r7'
}
butterknife-gradle-plugin 这里是butterknife插件 对应项目=>butterknife-gradle-plugin
com.jakewharton:butterknife:10.1.0 这里是使用的人调用的butterknife包 =>butterknife & butterknife-annotations & butterknife-runtime
com.jakewharton:butterknife-compiler 这个是注解解析器 => butterknife-compiler
整体流程:
1.先是从使用者通过注释添加程序进行使用,也就是使用注解。
2. 项目在进行构建的时候 通过 annotationProcessor 将对应的注解进行转换(JavaPoet) 成对应的java ->最终转成.class
3.程序在执行的时候通过调用 bk的相关函数,完成任务
source code ->.java ->.class -> .dex
(source code ->.java 代码编译期解析注解,并且生成新的 Java 文件,减少手动的代码输入)

核心用到的库:
javapoet:是用来生成.java文件 https://github.com/square/javapoet
auto-service:是google提供的,注解 Processor,对其生成配置信息
APT:Annotation Processing Tool 用于编译期处理注解的api组件,提供2部分:
1、用于模型化Java 程序语言结构的模型化api,包括com.sun.mirror包下的mirror api,javax.lang.model包下的element api 及其他辅助工具类。
2、javax.annotation.processing包下用于编写注解处理器的注解处理api。
https://docs.oracle.com/javase/7/docs/technotes/guides/apt/index.html
[java8 使用Pluggable Annotation Processing API 移除了apt]
按项目 进行源码拆解:
butterknife 这库是给应用引用的。
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
ButterKnife.bind(this);
}
ButterKnife.bind(this) 经过一系列的调用,最终调到以下函数
/**
**bindView使用@code source在指定的@code target中注释的字段和方法
*
* @param target Target class for view binding.
* @param source View root on which IDs will be looked up.
*/
@NonNull @UiThread
public static Unbinder bind(@NonNull Object target, @NonNull View source) {
Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
//实例化:执行它的构造函数 也就是 下文的MainActivity_ViewBinding()
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);
}
}
@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
//BINDINGS 是绑定的缓存,因为下面.getClassLoader().loadClass 类的反射,建立缓存,会尽量减少性能损失
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
if (bindingCtor != null || BINDINGS.containsKey(cls)) {
if (debug) Log.d(TAG, "HIT: Cached in binding map.");
return bindingCtor;
}
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 {
//加载clsName "_ViewBinding".java 这个是 【butterknife-compiler】注释编译器 在项目编译时生成的。
//在后面的会说明
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
//noinspection unchecked
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;
}
生成以下 ***_ViewBinding
public class MainActivity_ViewBinding implements Unbinder {
private MainActivity target;
private View view7f05001c;
@UiThread
public MainActivity_ViewBinding(MainActivity target) {
this(target, target.getWindow().getDecorView());
}
@UiThread
public MainActivity_ViewBinding(final MainActivity target, View source) {
……
}
@Override
@CallSuper
public void unbind() {
……
}
}
butterknife-compiler 这库在项目构建的时候调用的。主要就是将 注解 解析=>生成.java文件。
说白了主要是通过调 ButterKnifeProcessor 里的process 进行回调完成对注解的解析。butterknife主要是重写以下方法
init(ProcessingEnvironment processingEnvironment)
里面提供了Filer等工具类。注解处理器可以用Filer类创建新文件(源文件、类文件、辅助资源文件)。由此方法创建的源文件和类文件将由管理它们的工具(javac)处理。
getSupportedSourceVersion()
支持JDK的版本
public Set getSupportedAnnotationTypes()
获取需要处理的注解
public boolean process(annotationsannotations, RoundEnvironment roundEnv)
核心方法,在这里你可以扫描和处理注解,并生成java文件。
下面主要分析process处理机制
@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
//1.查找解析绑定元素 这个是以TypeElement (类,接口,Fragment为key),BindingSet是这个类上绑定集合
Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);
//2.遍历解析生成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;
}
step1:
private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();
Set<TypeElement> erasedTargetNames = new LinkedHashSet<>();
// Process each @BindAnim element. 解析bindAim元素、
for (Element element : env.getElementsAnnotatedWith(BindAnim.class)) {
//校验有效性
if (!SuperficialValidation.validateElement(element)) continue;
try {
parseResourceAnimation(element, builderMap, erasedTargetNames);
} catch (Exception e) {
logParsingError(element, BindAnim.class, e);
}
}
....省略些吧 基本一个套路
// Process each @BindView element.
// env.getElementsAnnotatedWith(BindView.class)获取所有使用BindView注解的元素
for (Element element : env.getElementsAnnotatedWith(BindView.class)) {
try {
parseBindView(element, builderMap, erasedTargetNames);
} catch (Exception e) {
logParsingError(element, BindView.class, e);
}
}
// Process each @BindViews element.
for (Element element : env.getElementsAnnotatedWith(BindViews.class)) {
// we don't SuperficialValidation.validateElement(element)
// so that an unresolved View type can be generated by later processing rounds
try {
parseBindViews(element, builderMap, erasedTargetNames);
} catch (Exception e) {
logParsingError(element, BindViews.class, e);
}
}
// Process each annotation that corresponds to a listener.
for (Class<? extends Annotation> listener : LISTENERS) {
findAndParseListener(env, listener, builderMap, erasedTargetNames);
}
// Associate superclass binders with their subclass binders. This is a queue-based tree walk
// which starts at the roots (superclasses) and walks to the leafs (subclasses).
// 将builderMap中的数据添加到队列中
Deque<Map.Entry<TypeElement, BindingSet.Builder>> entries =
new ArrayDeque<>(builderMap.entrySet());
Map<TypeElement, BindingSet> bindingMap = new LinkedHashMap<>();
while (!entries.isEmpty()) {
// 出队列
Map.Entry<TypeElement, BindingSet.Builder> entry = entries.removeFirst();
TypeElement type = entry.getKey();
BindingSet.Builder builder = entry.getValue();
// 查找当前类元素的父类元素
TypeElement parentType = findParentType(type, erasedTargetNames);
if (parentType == null) {
bindingMap.put(type, builder.build());
} else {
BindingSet parentBinding = bindingMap.get(parentType);
if (parentBinding != null) {
// 如果找到父类元素,则给当前类元素对应的BindingSet.Builder设置父BindingSet
builder.setParent(parentBinding);
bindingMap.put(type, builder.build());
} else {
// Has a superclass binding but we haven't built it yet. Re-enqueue for later.
entries.addLast(entry);
}
}
}
return bindingMap;
}
/**
* 解析bindview .
* @param element
* @param builderMap
* @param erasedTargetNames
*/
private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap,
Set<TypeElement> erasedTargetNames) {
// 首先要注意,此时element是VariableElement类型的,即成员变量
// enclosingElement是当前元素的父类元素,一般就是我们使用ButteKnife时定义的View类型成员变量所在的类,可以理解为之前例子中的MainActivity
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
// isInaccessibleViaGeneratedCode必须是类、非静态、非私有
// 是否在系统相关的资源中使用了ButteKnife注解
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();
// 判断当前元素是否是 View 的子类,或者是接口,不是的话抛出异常
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;
}
// 这里基本就是构建一个资源。
//@BindView(R.id.test) 也就是test在R.java里的资源值
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并返回,并且以enclosingElement 为key添加到builderMap中
builder = getOrCreateBindingBuilder(builderMap, enclosingElement);
}
String name = simpleName.toString();
TypeName type = TypeName.get(elementType);
boolean required = isFieldRequired(element);
builder.addField(resourceId, new FieldViewBinding(name, type, required));
// Add the type-erased version to the valid binding targets set.
erasedTargetNames.add(enclosingElement);
}
private BindingSet.Builder getOrCreateBindingBuilder(
Map<TypeElement, BindingSet.Builder> builderMap, TypeElement enclosingElement) {
BindingSet.Builder builder = builderMap.get(enclosingElement);
if (builder == null) {
builder = BindingSet.newBuilder(enclosingElement);
builderMap.put(enclosingElement, builder);
}
return builder;
}
static Builder newBuilder(TypeElement enclosingElement) {
TypeMirror typeMirror = enclosingElement.asType();
// 判断当前父元素的类型
boolean isView = isSubtypeOfType(typeMirror, VIEW_TYPE);
boolean isActivity = isSubtypeOfType(typeMirror, ACTIVITY_TYPE);
boolean isDialog = isSubtypeOfType(typeMirror, DIALOG_TYPE);
TypeName targetType = TypeName.get(typeMirror);
if (targetType instanceof ParameterizedTypeName) {
targetType = ((ParameterizedTypeName) targetType).rawType;
}
// 获取父类元素的包名
String packageName = getPackage(enclosingElement).getQualifiedName().toString();
// 获取父类元素的名称
String className = enclosingElement.getQualifiedName().toString().substring(
packageName.length() + 1).replace('.', '$');
// 生成 java类的名称
ClassName bindingClassName = ClassName.get(packageName, className + "_ViewBinding");
boolean isFinal = enclosingElement.getModifiers().contains(Modifier.FINAL);
return new Builder(targetType, bindingClassName, isFinal, isView, isActivity, isDialog);
}
开始生成.java ,通过JavaPoet
@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
//查找解析绑定元素
Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);
//遍历解析生成java step2
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;
}
step2: 根据对应依赖逻辑生成.java
JavaFile brewJava(int sdk, boolean debuggable) {
//TypeSpec 表示类、接口、或者枚举声明
TypeSpec bindingConfiguration = createType(sdk, debuggable);
return JavaFile.builder(bindingClassName.packageName(), bindingConfiguration)
.addFileComment("Generated code from Butter Knife. Do not modify!")
.build();
}
//这里就是生成 类的一些方法,变量,函数 。
private TypeSpec createType(int sdk, boolean debuggable) {
TypeSpec.Builder result = TypeSpec.classBuilder(bindingClassName.simpleName())
.addModifiers(PUBLIC);
if (isFinal) {
result.addModifiers(FINAL);
}
if (parentBinding != null) {
result.superclass(parentBinding.bindingClassName);
} else {
//实现unbind
result.addSuperinterface(UNBINDER);
}
if (hasTargetField()) {
result.addField(targetTypeName, "target", PRIVATE);
}
if (isView) { //如果是ivew
result.addMethod(createBindingConstructorForView());
} else if (isActivity) {//如果是activity
result.addMethod(createBindingConstructorForActivity());
} else if (isDialog) {//如果是dialog
result.addMethod(createBindingConstructorForDialog());
}
if (!constructorNeedsView()) {
// Add a delegating constructor with a target type + view signature for reflective use.
result.addMethod(createBindingViewDelegateConstructor());
}
result.addMethod(createBindingConstructor(sdk, debuggable));
if (hasViewBindings() || parentBinding == null) {
result.addMethod(createBindingUnbindMethod(result));
}
return result.build();
}
小结:
ButterKnife 整个过程是在项目编译阶段完成的,主要用到了 annotationProcessor 和 JavaPoet 技术,使用时通过生成的辅助类完成操作,调用bind(this)里面 调用了反射,但通过cache缓存,减少性能损耗,很多人都说运行时没有用到反射、其实是不对的。
以上分析是主要核心代码流程,关于Butterknife里面的所有的类,不作过多的讨论,都是围绕一个目标进行的,如果还想再细点研究,可以把源代码带入项目进行调试。
来源:oschina
链接:https://my.oschina.net/u/3982963/blog/3021144