Butterknife的源碼之前只是單純的知道大致的一個(gè)思路,沒(méi)有具體看過(guò)他的實(shí)現(xiàn)步驟。因作此文已記之。
Ox1 init :對(duì)外暴露的初始函數(shù)
@Override public synchronized void init(ProcessingEnvironment env) {
super.init(env);
//獲取sdk版本 ,注解器的配置在 getSupportedOptions() 中查看
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.");
}
}
//是否debug模式
debuggable = !"false".equals(env.getOptions().get(OPTION_DEBUGGABLE));
//獲取相對(duì)應(yīng)的工具對(duì)象
elementUtils = env.getElementUtils();
typeUtils = env.getTypeUtils();
filer = env.getFiler();
try {
trees = Trees.instance(processingEnv);
} catch (IllegalArgumentException ignored) {
}
}
//配置了注解處理器所需要的入?yún)? @Override public Set<String> getSupportedOptions() {
return ImmutableSet.of(OPTION_SDK_INT, OPTION_DEBUGGABLE);
}
getSupportedIptions的配置項(xiàng)在主工程的build.gradle
android {
javaCompileOptions {
annotationProcessorOptions {
arguments = [debug: "2333"] //參數(shù)只能是字符串
}
}
}
總結(jié)以上代碼:
- 獲取配置項(xiàng)
- 獲取相關(guān)的工具類(lèi)的實(shí)例
Ox2 process :處理注解的主體方法
@Override public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
//獲取注解內(nèi)容的綁定關(guān)系
Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);
//循環(huán)遍歷
for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
TypeElement typeElement = entry.getKey();
BindingSet binding = entry.getValue();
//生成java代碼
JavaFile javaFile = binding.brewJava(sdk, debuggable);
try {
//創(chuàng)建java文件
javaFile.writeTo(filer);
} catch (IOException e) {
error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
}
}
return false;
}
以上的流程有兩個(gè)疑惑點(diǎn):
- BindingSet:這個(gè)類(lèi)是作用是?
- findAndParseTargets:如何獲取到注解內(nèi)容的結(jié)果的?
我們先來(lái)看看BindingSet的核心代碼: constructor & brewJava
//通過(guò)Budilder 構(gòu)造,對(duì)參數(shù)進(jìn)行配置
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;
}
//根據(jù)上面的配置,生成了相對(duì)應(yīng)代碼
JavaFile brewJava(int sdk, boolean debuggable) {
return JavaFile.builder(bindingClassName.packageName(), createType(sdk, debuggable))
.addFileComment("Generated code from Butter Knife. Do not modify!")
.build();
}
總結(jié)以上代碼:
BindingSet 大致的作用就是
- 一個(gè)文件所需要的所有綁定關(guān)系
- 生成的代碼的函數(shù)(通過(guò)JavaPoet)
所以剩下的就在 findAndParseTargets 這個(gè)方法內(nèi)了
private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();
Set<TypeElement> erasedTargetNames = new LinkedHashSet<>();
scanForRClasses(env);
// Process each @BindColor element.
for (Element element : env.getElementsAnnotatedWith(BindColor.class)) {
if (!SuperficialValidation.validateElement(element)) continue;
try {
parseResourceColor(element, builderMap, erasedTargetNames);
} catch (Exception e) {
logParsingError(element, BindColor.class, e);
}
}
// Process each @BindView element.
for (Element element : env.getElementsAnnotatedWith(BindView.class)) {
// we don't SuperficialValidation.validateElement(element)
// so that an unresolved View type can be generated by later processing rounds
try {
parseBindView(element, builderMap, erasedTargetNames);
} catch (Exception e) {
logParsingError(element, BindView.class, e);
}
}
(以下省略其他 類(lèi)似 @BindXXX 代碼)
...
// 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).
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;
總結(jié)以上代碼:
- 處理注解的信息
- 將結(jié)果組合成bindingMap
以上的 parseXXX 方法實(shí)現(xiàn)邏輯大體一致,因此我們只需要看其中的一個(gè)細(xì)分方法就ok了
Ox3 parseBindView :處理 @BindView注解 信息
為了讓大家更好的理解,我們先寫(xiě)一個(gè)demo
package com.example
public class MainActivity extends AppCompatActivity {
@BindView(R.id.text)
TextView tvTest
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
以下將會(huì)以上面的demo的情況來(lái)進(jìn)行講解
private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap,
Set<TypeElement> erasedTargetNames) {
//獲取 MainActivity
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
//判斷 修飾符不為private 字段;注解不使用在Android、java的包內(nèi)
boolean hasError = isInaccessibleViaGeneratedCode(BindView.class, "fields", element)
|| isBindingInWrongPackage(BindView.class, element);
//獲取 TextView
TypeMirror elementType = element.asType();
//todo
if (elementType.getKind() == TypeKind.TYPEVAR) {
TypeVariable typeVariable = (TypeVariable) elementType;
elementType = typeVariable.getUpperBound();
}
//獲取 com.example.MainActivity
Name qualifiedName = enclosingElement.getQualifiedName();
//獲取 tvTest
Name simpleName = element.getSimpleName();
//判斷 是否為View的子類(lèi);是否為接口類(lèi)型
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;
}
}
//報(bào)錯(cuò)則退出
if (hasError) {
return;
}
//獲取注解上的id
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);
}