新博客地址
在學習了XUtils的注入方式之後,看了下ButterKnife的實現方式,結果發現完全不一樣,然後借鑑網上的博客,結果發現用的都是Eclipse以及舊版本的ButterKnife進行實現的。
這裏我用AndroidStudio根據ButterKnife的版本進行了實現。
文字枯燥,還是先看下butterknife的module圖:
先看下ButterKnife中生成的java代碼(看看騷包的註釋 Do not modify!)
// Generated code from Butter Knife. Do not modify!
package com.example.butterknife;
import android.view.View;
import android.widget.AdapterView;
import butterknife.ButterKnife;
import butterknife.internal.DebouncingOnClickListener;
import butterknife.internal.Finder;
import butterknife.internal.Utils;
import butterknife.internal.ViewBinder;
import java.lang.IllegalStateException;
import java.lang.Object;
import java.lang.Override;
import java.lang.SuppressWarnings;
public class SimpleActivity$$ViewBinder<T extends SimpleActivity> implements ViewBinder<T> {
@Override
public void bind(final Finder finder, final T target, Object source) {
Unbinder unbinder = createUnbinder(target);
View view;
view = finder.findRequiredView(source, 2130968576, "field 'title'");
target.title = finder.castView(view, 2130968576, "field 'title'");
view = finder.findRequiredView(source, 2130968577, "field 'subtitle'");
target.subtitle = finder.castView(view, 2130968577, "field 'subtitle'");
view = finder.findRequiredView(source, 2130968578, "field 'hello', method 'sayHello', and method 'sayGetOffMe'");
target.hello = finder.castView(view, 2130968578, "field 'hello'");
unbinder.view2130968578 = view;
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
target.sayHello();
}
});
view.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View p0) {
return target.sayGetOffMe();
}
});
view = finder.findRequiredView(source, 2130968579, "field 'listOfThings' and method 'onItemClick'");
target.listOfThings = finder.castView(view, 2130968579, "field 'listOfThings'");
unbinder.view2130968579 = view;
((AdapterView<?>) view).setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> p0, View p1, int p2, long p3) {
target.onItemClick(p2);
}
});
view = finder.findRequiredView(source, 2130968580, "field 'footer'");
target.footer = finder.castView(view, 2130968580, "field 'footer'");
target.headerViews = Utils.listOf(
finder.<View>findRequiredView(source, 2130968576, "field 'headerViews'"),
finder.<View>findRequiredView(source, 2130968577, "field 'headerViews'"),
finder.<View>findRequiredView(source, 2130968578, "field 'headerViews'"));
target.unbinder = unbinder;
}
@SuppressWarnings("unchecked")
protected <U extends Unbinder<T>> U createUnbinder(T target) {
return (U) new Unbinder(target);
}
@SuppressWarnings("unchecked")
protected <U extends Unbinder<T>> U accessUnbinder(T target) {
return (U) target.unbinder;
}
public static class Unbinder<T extends SimpleActivity> implements ButterKnife.ViewUnbinder<T> {
private T target;
View view2130968578;
View view2130968579;
protected Unbinder(T target) {
this.target = target;
}
@Override
public final void unbind() {
if (target == null) throw new IllegalStateException("Bindings already cleared.");
unbind(target);
target = null;
}
protected void unbind(T target) {
target.title = null;
target.subtitle = null;
view2130968578.setOnClickListener(null);
view2130968578.setOnLongClickListener(null);
target.hello = null;
((AdapterView<?>) view2130968579).setOnItemClickListener(null);
target.listOfThings = null;
target.footer = null;
target.headerViews = null;
target.unbinder = null;
}
}
}
ButterKnifeView注入demo(簡單易學)
這個demo的代碼生成,以及接口思想,以及調用生成代碼的思想,都是借鑑ButterKnife的
我先簡單實現了ButterKnife的View注入,去除了ButterKnife繁瑣的編譯校驗,本篇也沒有添加事件注入(下一篇會有)
XUtils 與 ButterKnife實現方式的區別
上篇XUtils實現註解初始化用的是反射 + 動態代理
JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。
APT
而ButterKnife使用的是APT (Annotation Processing Tool )
apt是一個命令行工具,與之配套的還有一套用來描述程序語義結構的Mirror API。Mirror API(com.sun.mirror.*)描述的是程序在編譯時刻的靜態結構。通過Mirror API可以獲取到被註解的Java類型元素的信息,從而提供相應的處理邏輯。具體的處理工作交給apt工具來完成。編寫註解處理器的核心是AnnotationProcessorFactory和AnnotationProcessor兩個接口。後者表示的是註解處理器,而前者則是爲某些註解類型創建註解處理器的工廠。
APT配置
需要再compiler Module中的main.resources.META-INF.services下新建文本java.annotation.processing.Processor,在文本內添加Processor全名(我的Processor是com.example.MyProcessor)
或者直接在Processor實現類上@AutoService(Processor.class)(我還沒實現成功)
需要在build.gradle裏面配置APT插件(classpath ‘com.neenbedankt.gradle.plugins:android-apt:1.8’)Processor類
(1)JAVA1.6以後的Processor實現類都需要extends AbstractProcessor
(2)Processor類需要指定其處理的註解
方式一(類上註解):@SupportedAnnotationTypes(“com.example.AptBetter”)
方式二(重寫方法):getSupportedAnnotationTypes
(4)process方法,編譯時進入的主方法,用於分析註解信息,以及生成代碼
(4)Processor都要指定版本
一般獲取最新的版本:@Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); }
Process內部元素
ProcessingEnvironment:編譯環境// 用於生成代碼 Filer filer = processingEnv.getFiler(); // 用於編譯時給圖 Messager messager = processingEnv.getMessager(); // Element工具,可以獲取包名 Elements elementUtils = processingEnv.getElementUtils();
RoundEnvironment:回合環境(國內暫時沒有固定的說法,暫且定義爲元素環境)
因爲可以從中獲取到5大ElementMirror的五大Element PackageElement 包元素 TypeElement 類元素 VariableElement 變量元素 ExecutableElement 方法元素 TypeParameterElement 參數元素 通過這些元素,可以獲取到元素的各種信息:註解、類型、名稱等等
其中TypeMirror.accept這個我還很模糊,有明白的請賜教
代碼生成器
這裏有一個大殺器:JavaPoet (Java詩人) 也是Jake Wharton所參與的著作之一,膜拜Jake Wharthon,膜拜Square。大家可以直接到Github上搜索JavaPoet。
運行期流程
主要是通過Butterknife調用生成的Activity$$ViewBinder類中的bind方法,實現了控件初始化、時間注入
Question :
(1)在代碼編寫的時候類都還沒有生成,那麼如何獲取其對象
answer:通過反,Activity也是通過這種方式生成的
Class<?> viewBindingClass = Class.forName(clsName + "$$ViewBinder");
ViewBinder viewBinder = (ViewBinder<Object>) viewBindingClass.newInstance();
(2)如何能調用bind方法,如何能強轉,因爲生成的代碼實現了ViewBinder接口,接口的力量啊!針對接口編程!
分析compiler
思路分析:
(1)一個類、一個內部類生成一個同包下的名稱相似的IOC容器類
(2)一類中會有很多控件需要初始化,那麼我們把一個類、一個內部類所提供的多個控件信息、類名、包名、id封裝成一個Bean:BindingClass(ButterKnife除了可以給控件初始化之外,還可以給各種資源初始化,甚至可以解綁UnBinding,ButterKnife中把每種類型都封裝成一種Bean,這裏我們緊緊做了View的初始化),我們這裏BindingClass裏面僅僅放的是FieldViewBinding
(3)一個需要初始化的類對應一個BindingClass(含有很多FieldViewBinding),對應生成一個ViewBinder,那麼生成代碼的邏輯我們可以放在Compiler中的BindingClass,作爲其內部類(可以直接獲取BindingClass中的類名、包名、控件集合)
編譯器讀信息
// 所有AptBetter的成員變量
Set<? extends Element> elementsAnnotatedWith = roundEnv.getElementsAnnotatedWith(AptBetter.class);
// 元素歸類
for (Element element : elementsAnnotatedWith) {
TypeElement typeElement = (TypeElement) element.getEnclosingElement();
BindingClass bindingClass = null;
if (allBindEvent.containsKey(typeElement)) {
bindingClass = allBindEvent.get(typeElement);
} else {
String targetType = typeElement.getQualifiedName().toString();
String classPackage = getPackageName(typeElement);
// packageName$className$$ViewBinder
String className = getClassName(typeElement, classPackage) + SUFFIX;
bindingClass = new BindingClass(classPackage, className, targetType);
allBindEvent.put(typeElement, bindingClass);
}
int value = element.getAnnotation(AptBetter.class).value();
String elementName = element.getSimpleName().toString();
TypeName typeName = TypeName.get(element.asType());
bindingClass.addField(value, new FieldViewBinding(elementName, typeName, value));
}
代碼生成(JavaPoet)
這裏是用的是JavaPoet,下面是教程地址: