原理:
APT(Annotation Processing Tool)編譯時(shí)解析技術(shù)(現(xiàn)在已經(jīng)改成了谷歌的更強(qiáng)大的annotationProcessor乒融,APT已經(jīng)停止更新了)就是你聲明的注解的生命周期為CLASS,然后繼承AbstractProcessor類斋配。繼承這個類后,在編譯的時(shí)候真椿,編譯器會掃描所有帶有你要處理的注解的類,然后再調(diào)用AbstractProcessor的process方法,對注解進(jìn)行處理崇呵,那么我們就可以在處理的時(shí)候出爹,動態(tài)生成綁定事件或者控件的java代碼庄吼,然后在運(yùn)行的時(shí)候,直接調(diào)用方法完成綁定
ButterKnife源碼解析
不同版本源碼區(qū)別比較大严就,這里看的是8.6.0版本总寻,對比了和7.0.1 入口差很多!
直接就從 ButterKnife.bind(this)入手吧,點(diǎn)進(jìn)來看看:
@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
View sourceView = target.getWindow().getDecorView();
return createBinding(target, sourceView);
}
再點(diǎn)到createBinding(target, sourceView)里面看看:
private static Unbinder createBinding(@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 {
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);
}
}
createBinding()方法主要就是拿到我們綁定的Activity的Class盈蛮,然后通過Constructor構(gòu)造器獲取一個Unbinder子類的構(gòu)造方法废菱,然后在調(diào)用newInstance(target, source)通過構(gòu)造方法獲取到Unbinder子類的一個實(shí)例,這里傳入兩個參數(shù)抖誉,說明構(gòu)造方法里需要兩個參數(shù)殊轴。我們打開findBindingConstructorForClass()方法:
@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
if (bindingCtor != null) {
if (debug) Log.d(TAG, "HIT: Cached in binding map.");
return bindingCtor;
}
String clsName = cls.getName();
if (clsName.startsWith("android.") || clsName.startsWith("java.")) {
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return null;
}
try {
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;
}
上面的BINDINGS是一個保存了Class為key,Class_ViewBinding為Value的一個LinkedHashMap,主要是做一下緩存,提高下次再來bind的性能袒炉。
第14行clsName 是我們傳入要綁定的Activity類名旁理,第16行通過反射調(diào)用構(gòu)造方法,這里相當(dāng)于拿到了Activity_ViewBinding這個類的實(shí)例我磁。其實(shí)從類名可以看出來孽文,相當(dāng)于Activity的一個輔助類,這時(shí)候我們就要問了夺艰,我們在用的時(shí)候沒有聲明這個類坝罂蕖?從哪里來的郁副? 不要方减牺,其實(shí)它就是我們在之前講原理的時(shí)候說到的AbstractProcessor在編譯的時(shí)候生成的一個類,我們后面再來看它存谎,現(xiàn)在我們繼續(xù)往下面分析拔疚。
前面我們說到,這個方法里面用linkhashMap做了下緩存既荚,所以在下邊稚失,就把剛剛反射的bindingCtor作為value,Class作為key加入這個LinkedHashMap,下次再綁定這個類的時(shí)候恰聘,就直接在方法的開始的時(shí)候取出來用
再看下面build 生成的類
剛才說的Unbinder的一個子類句各,看這里的類名∥迹現(xiàn)在應(yīng)該懂了。它剛好是實(shí)現(xiàn)了Unbinder接口诫钓。之前說了通過反射拿到了Activity_ViewBinding這個類的構(gòu)造方法即通過調(diào)用getConstructor(cls, View.class)方法旬昭,然后通過newInstance(target, source)方法創(chuàng)建實(shí)例,這里傳入的兩個參數(shù)就是我們MainActivity_ViewBinding(final MainActivity target, View source)里面的兩個參數(shù)菌湃。因?yàn)槲覀兛梢栽贏ctivity中使用butterknife,也可以在Fragment和Adapter等中使用butterknife问拘,那么在不同的地方使用butterknife,這個target也就不同惧所。我們接著看里面的findRequiredView方法:
public static View findRequiredView(View source, @IdRes int id, String who) {
View view = source.findViewById(id);
if (view != null) {
return view;
}
String name = getResourceEntryName(source, id);
throw new IllegalStateException("Required view '"
+ name
+ "' with ID "
+ id
+ " for "
+ who
+ " was not found. If this view is optional add '@Nullable' (fields) or '@Optional'"
+ " (methods) annotation.");
}
還是使用的findViewById骤坐。
返回上面的MainActivity_ViewBinding代碼,首先調(diào)用了findRequiredView方法下愈,其實(shí)這個方法最后經(jīng)過處理就是調(diào)用了findViewById方法纽绍,拿到相應(yīng)的view,然后再賦值給target.tv势似,剛說了target就是那個要綁定的Activity拌夏,這里通過 target.tv 這樣的調(diào)用方式,說明了Activity中不能把TextView設(shè)置為private履因,不然會報(bào)錯障簿,其實(shí)這里可以用反射來拿到textView的,這里應(yīng)該也是為了性能著想栅迄。最后setOnClickListener站故,DebouncingOnClickListener這個Listener其實(shí)也是實(shí)現(xiàn)了View.OnClickListener 方法,然后在OnClick里面調(diào)用了doClick方法毅舆。
Butterknife在編譯的時(shí)候生成代碼的原理
com.jakewharton:butterknife-compiler 就是自定義的注解處理器西篓,我們在 Gradle 中注冊使用它。
然而我在項(xiàng)目結(jié)構(gòu)中找了很久也沒有找到這個庫的文件憋活,有可能是在編譯時(shí)才去訪問的岂津,如果需要可以在 GitHub 中找到:
butterknife-compiler
我們看看ButterKnifeProcessor這個類:
@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));
elementUtils = env.getElementUtils();
typeUtils = env.getTypeUtils();
filer = env.getFiler();
try {
trees = Trees.instance(processingEnv);
} catch (IllegalArgumentException ignored) {
}
}
int()方法里面進(jìn)來判斷了最低的支持的sdk版本。ProcessingEnviroment參數(shù)提供很多有用的工具類Elements, Types和Filer悦即。Types是用來處理TypeMirror的工具類寸爆,F(xiàn)iler用來創(chuàng)建生成輔助文件。至于ElementUtils嘛盐欺,其實(shí)ButterKnifeProcessor在運(yùn)行的時(shí)候,會掃描所有的Java源文件仅醇,然后每一個Java源文件的每一個部分都是一個Element冗美,比如一個包、類或者方法析二。
@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(BindAnim.class);
annotations.add(BindArray.class);
annotations.add(BindBitmap.class);
annotations.add(BindBool.class);
annotations.add(BindColor.class);
annotations.add(BindDimen.class);
annotations.add(BindDrawable.class);
annotations.add(BindFloat.class);
annotations.add(BindFont.class);
annotations.add(BindInt.class);
annotations.add(BindString.class);
annotations.add(BindView.class);
annotations.add(BindViews.class);
annotations.addAll(LISTENERS);
return annotations;
}
getSupportedAnnotationTypes()方法主要是指定ButterknifeProcessor是注冊給哪些注解的粉洼。我們可以看到节预,在源代碼里面,作者一個一個地把Class文件加到那個LinkedHashSet里面属韧,然后再把LISTENERS也全部加進(jìn)去安拟。
其實(shí)整個類最重要的是process方法:
@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);
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;
}
這個方法的作用主要是掃描、評估和處理我們程序中的注解宵喂,然后生成Java文件糠赦,也就是前面說的MainActivity_ViewBinding。首先一進(jìn)這個函數(shù)就調(diào)用了findAndParseTargets方法锅棕,我們就去看看findAndParseTargets方法到底做了什么:
private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();
Set<TypeElement> erasedTargetNames = new LinkedHashSet<>();
scanForRClasses(env);
// Process each @BindAnim element.
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);
}
}
........
........
........
// 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).
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) {
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;
這個方法的代碼非常多拙泽,這里只貼出一部分,這個方法的主要的流程如下:
掃描所有具有注解的類裸燎,然后根據(jù)這些類的信息生成BindingSet顾瞻,最后生成以TypeElement為鍵,BindingSet為值的鍵值對。
循環(huán)遍歷這個鍵值對德绿,根據(jù)TypeElement和BindingSet里面的信息生成對應(yīng)的java類荷荤。例如AnnotationActivity生成的類即為MainActivity_ViewBinding類。
這里我們可以看看BindingSet里面的代碼:
final class BindingSet {
static final ClassName UTILS = ClassName.get("butterknife.internal", "Utils");
private static final ClassName VIEW = ClassName.get("android.view", "View");
private static final ClassName CONTEXT = ClassName.get("android.content", "Context");
private static final ClassName RESOURCES = ClassName.get("android.content.res", "Resources");
private static final ClassName UI_THREAD =
ClassName.get("android.support.annotation", "UiThread");
private static final ClassName CALL_SUPER =
ClassName.get("android.support.annotation", "CallSuper");
private static final ClassName SUPPRESS_LINT =
ClassName.get("android.annotation", "SuppressLint");
private static final ClassName UNBINDER = ClassName.get("butterknife", "Unbinder");
static final ClassName BITMAP_FACTORY = ClassName.get("android.graphics", "BitmapFactory");
static final ClassName CONTEXT_COMPAT =
ClassName.get("android.support.v4.content", "ContextCompat");
static final ClassName ANIMATION_UTILS =
ClassName.get("android.view.animation", "AnimationUtils");
private final TypeName targetTypeName;
private final ClassName bindingClassName;
private final boolean isFinal;
private final boolean isView;
private final boolean isActivity;
private final boolean isDialog;
private final ImmutableList<ViewBinding> viewBindings;
private final ImmutableList<FieldCollectionViewBinding> collectionBindings;
private final ImmutableList<ResourceBinding> resourceBindings;
private final BindingSet parentBinding;
private BindingSet(TypeName targetTypeName, ClassName bindingClassName, boolean isFinal,
boolean isView, boolean isActivity, boolean isDialog, ImmutableList<ViewBinding> viewBindings,
ImmutableList<FieldCollectionViewBinding> collectionBindings,
ImmutableList<ResourceBinding> resourceBindings, BindingSet parentBinding) {
this.isFinal = isFinal;
this.targetTypeName = targetTypeName;
this.bindingClassName = bindingClassName;
this.isView = isView;
this.isActivity = isActivity;
this.isDialog = isDialog;
this.viewBindings = viewBindings;
this.collectionBindings = collectionBindings;
this.resourceBindings = resourceBindings;
this.parentBinding = parentBinding;
}
JavaFile brewJava(int sdk, boolean debuggable) {
return JavaFile.builder(bindingClassName.packageName(), createType(sdk, debuggable))
.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);
}
........
........
........
static final class Builder {
private final TypeName targetTypeName;
private final ClassName bindingClassName;
private final boolean isFinal;
private final boolean isView;
private final boolean isActivity;
private final boolean isDialog;
private BindingSet parentBinding;
private final Map<Id, ViewBinding.Builder> viewIdMap = new LinkedHashMap<>();
private final ImmutableList.Builder<FieldCollectionViewBinding> collectionBindings =
ImmutableList.builder();
private final ImmutableList.Builder<ResourceBinding> resourceBindings = ImmutableList.builder();
private Builder(TypeName targetTypeName, ClassName bindingClassName, boolean isFinal,
boolean isView, boolean isActivity, boolean isDialog) {
this.targetTypeName = targetTypeName;
this.bindingClassName = bindingClassName;
this.isFinal = isFinal;
this.isView = isView;
this.isActivity = isActivity;
this.isDialog = isDialog;
}
void addField(Id id, FieldViewBinding binding) {
getOrCreateViewBindings(id).setFieldBinding(binding);
}
void addFieldCollection(FieldCollectionViewBinding binding) {
collectionBindings.add(binding);
}
boolean addMethod(
Id id,
ListenerClass listener,
ListenerMethod method,
MethodViewBinding binding) {
ViewBinding.Builder viewBinding = getOrCreateViewBindings(id);
if (viewBinding.hasMethodBinding(listener, method) && !"void".equals(method.returnType())) {
return false;
}
viewBinding.addMethodBinding(listener, method, binding);
return true;
}
void addResource(ResourceBinding binding) {
resourceBindings.add(binding);
}
void setParent(BindingSet parent) {
this.parentBinding = parent;
}
String findExistingBindingName(Id id) {
ViewBinding.Builder builder = viewIdMap.get(id);
if (builder == null) {
return null;
}
FieldViewBinding fieldBinding = builder.fieldBinding;
if (fieldBinding == null) {
return null;
}
return fieldBinding.getName();
}
private ViewBinding.Builder getOrCreateViewBindings(Id id) {
ViewBinding.Builder viewId = viewIdMap.get(id);
if (viewId == null) {
viewId = new ViewBinding.Builder(id);
viewIdMap.put(id, viewId);
}
return viewId;
}
BindingSet build() {
ImmutableList.Builder<ViewBinding> viewBindings = ImmutableList.builder();
for (ViewBinding.Builder builder : viewIdMap.values()) {
viewBindings.add(builder.build());
}
return new BindingSet(targetTypeName, bindingClassName, isFinal, isView, isActivity, isDialog,
viewBindings.build(), collectionBindings.build(), resourceBindings.build(),
parentBinding);
}
}
}
這個類的代碼也非常多移稳,所以我們也只貼一部分蕴纳,可以自己去看看源碼,這個BindingSet是管理了所有關(guān)于這個注解的一些信息還有實(shí)例本身的信息秒裕。
因?yàn)槲覀冎坝玫睦邮墙壎ǖ囊粋€View袱蚓,所以我們就只貼了解析View的代碼。好吧几蜻,這里遍歷了所有帶有@BindView的Element喇潘,然后對每一個Element進(jìn)行解析,也就進(jìn)入了parseBindView這個方法中
private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap,
Set<TypeElement> erasedTargetNames) {
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
// Start by verifying common generated code restrictions.
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;
}
// Assemble information on the field.
int id = element.getAnnotation(BindView.class).value();
BindingSet.Builder builder = builderMap.get(enclosingElement);
QualifiedId qualifiedId = elementToQualifiedId(element, id);
if (builder != null) {
String existingBindingName = builder.findExistingBindingName(getId(qualifiedId));
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 {
builder = getOrCreateBindingBuilder(builderMap, enclosingElement);
}
String name = simpleName.toString();
TypeName type = TypeName.get(elementType);
boolean required = isFieldRequired(element);
builder.addField(getId(qualifiedId), new FieldViewBinding(name, type, required));
// Add the type-erased version to the valid binding targets set.
erasedTargetNames.add(enclosingElement);
}
然后這里從一進(jìn)入這個方法到
int id = element.getAnnotation(BindView.class).value();
都是在拿到注解信息梭稚,然后驗(yàn)證注解的target的類型是否繼承自view颖低,然后上面這一行代碼獲得我們要綁定的View的id,再從builderMap里面取出BindingSet.Builder對象(這個BindingSet是管理了所有關(guān)于這個注解的一些信息還有實(shí)例本身的信息弧烤,其實(shí)最后是通過BindingSet來生成java代碼的忱屑,上面也已經(jīng)看了BindingSet的代碼),如果builderMap里面不存在的話暇昂,就在
builder = getOrCreateBindingBuilder(builderMap, enclosingElement);
這里生成一個莺戒,我們進(jìn)去看一下getOrCreateBindingBuilder:
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;
}
這里面其實(shí)很簡單,就是獲取一些這個注解所修飾的變量的一些信息急波,然后把這個解析后的builder加入到builderMap里面从铲。
返回剛剛的parseBindView中,根據(jù)view的信息生成一個FieldViewBinding澄暮,最后添加到上邊生成的builder實(shí)例中名段。這里基本完成了解析工作阱扬。最后回到findAndParseTargets中:
// 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).
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) {
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);
}
}
}
這里主要的工作是建立上面的綁定的所有的實(shí)例的解綁的關(guān)系,因?yàn)槲覀兘壎松毂伲詈笤诖a中還是會解綁的麻惶。這里預(yù)先處理好了這些關(guān)系。
回到我們的process中信夫, 現(xiàn)在解析完了annotation,該生成java文件了窃蹋,再看看代碼:
@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);
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;
}
遍歷剛剛得到的bindingMap,然后再一個一個地通過
javaFile.writeTo(filer);
來生成java文件忙迁。然而生成的java文件也是根據(jù)上面的信息來用字符串拼接起來的脐彩,然而這個工作在brewJava()中完成了:
JavaFile brewJava(int sdk, boolean debuggable) {
return JavaFile.builder(bindingClassName.packageName(), createType(sdk, debuggable))
.addFileComment("Generated code from Butter Knife. Do not modify!")
.build();
}
最后通過writeTo(Filer filer)生成java源文件。
上面的是一個大體流程姊扔,下面梳理下重點(diǎn):
1Butterknife是編譯期生成代碼方法惠奸,對運(yùn)行沒副作用,不像反射Afinal框架恰梢。
2.我們在MainActivity中寫的@Bindview(R.id.text) TextView tv;自動生成了
target.bt = (TextView)Utils.findRequiredViewAsType(source, 2131427423, "field \'bt\'", TextView.class);
這個過程就是注解處理器完成的佛南。Butterknife是怎么實(shí)現(xiàn)的,
@AutoServer(Processor.class)
public final class ButterknifeProcessor extends AbstructProcessor{
.....
}
AutoServer這個是谷歌的一個開源項(xiàng)目嵌言,自動生成 而 Butterknife的核心就是他的process(){
1.findAnd ParseTagets() 去解析注解
2.去遍歷1得到的map集合嗅回,
3.拿到上面的鍵值對的值也就是binding的class 再調(diào)writeTo方法,正真生成代碼
}