参考1
参考2
参考3
参考4
一:基本原理
编译时注解+APT
编译时注解:Rentention为CLASS的直接。保留时间是编译期。
APT(Annotation Processing Tool)编译时解析技术)。
声明的注解的生命周期为CLASS,然后创建注解处理器,即继承AbstractProcessor类。继承这个类后,在编译的时候,编译器会扫描所有带有你要处理的注解的类,然后再调用AbstractProcessor的process方法,对注解进行处理,处理完后,动态生成绑定事件或者控件的java代码,然后在运行的时候,直接调用bind
方法完成绑定。
二:bind过程
(1)@Bind注解
@Retention(CLASS) @Target(FIELD)
public @interface Bind {
/** View ID to which the field will be bound. */
int[] value();
}
(2)ButterKnife.bind()
public static void bind(Activity target) {
bind(target, target, Finder.ACTIVITY);
}
static void bind(Object target, Object source, Finder finder) {
Class> targetClass = target.getClass();
try {
if (debug) Log.d(TAG, "Looking up view binder for " + targetClass.getName());
ViewBinder
(1) 获取Activity的class对象
(2)通过findViewBinderForClass()找到该Activity对应的ViewBinder
(3)调用ViewBinder.bind()方法
(3) ButterKnife.findViewBinderForClass()
private static ViewBinder
(1)从缓存LinkedHashMap中取ViewBinder,若能取出,则说明ViewBinder已经生成过。
(2) 否则 得到ViewBinder的子类的类名(MainActivity$$ViewBinder),然后通过反射的形式创建(MainActivity$$ViewBinder)的实例,并存入缓存。
Tips: ViewBinder
public interface ViewBinder {
void bind(Finder finder, T target, Object source);
void unbind(T target);
}
举例说明
public class MainActivity extends AppCompatActivity {
@Bind(R.id.text_view)
TextView textView;
@OnClick(R.id.text_view)
void onClick(View view) {
textView.setText("我被click了");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
textView.setText("我还没有被click");
}
}
反编译代码
源代码就多了一个类MainActivity$$ViewBinder
public class MainActivity$$ViewBinder
implements ButterKnife.ViewBinder{
public void bind(ButterKnife.Finder paramFinder,
final T paramT, Object paramObject) {
View localView = (View)paramFinder.findRequiredView(paramObject,
2131492944, "field 'textView' and method 'onClick'");
paramT.textView = ((TextView)paramFinder.castView(localView,
2131492944, "field 'textView'"));
localView.setOnClickListener(new DebouncingOnClickListener() {
public void doClick(View paramAnonymousView) {
paramT.onClick(paramAnonymousView);
}
});
}
public void unbind(T paramT) {
paramT.textView = null;
}
}
首先调用了Finder的findRequiredView方法,其实这个方法最后经过处理就是调用了findView方法,拿到相应的view,然后再赋值给paramT.textView,paramT就是那个要绑定的Activity
(4)ButterKnife.Finder(enum)
public enum Finder {
VIEW {
@Override protected View findView(Object source, int id) {
return ((View) source).findViewById(id);
}
@Override public Context getContext(Object source) {
return ((View) source).getContext();
}
},
ACTIVITY {
@Override protected View findView(Object source, int id) {
return ((Activity) source).findViewById(id);
}
@Override public Context getContext(Object source) {
return (Activity) source;
}
},
DIALOG {
@Override protected View findView(Object source, int id) {
return ((Dialog) source).findViewById(id);
}
@Override public Context getContext(Object source) {
return ((Dialog) source).getContext();
}
};
三:APT编译期生成代码的流程
ButterknifeProcessor:注解处理器继承于AbstractProcessor
(1)在实现AbstractProcessor后,process()方法是必须实现的
(2)一般会实现getSupportedAnnotationTypes()和getSupportedSourceVersion();这两个方法一个返回支持的注解类型,一个返回支持的源码版本,参考上面的代码,写法基本是固定的。
(3)除此以外,我们还会选择复写init()方法,该方法传入一个参数processingEnv,可以帮助我们去初始化一些辅助类:
Filer mFileUtils: 跟文件相关的辅助类,生成JavaSourceCode.
Elements mElementUtils:跟元素相关的辅助类,帮助我们去获取一些元素相关的信息。
Messager mMessager:跟日志相关的辅助类。
(1) ButterknifeProcessor.int()
@Override
public synchronized void init(ProcessingEnvironment env) {
super.init(env);
elementUtils = env.getElementUtils();
typeUtils = env.getTypeUtils();
filer = env.getFiler();
}
(2) ButterknifeProcessor.getSupportAnnotationTypes()
BindArray:一个类对象,代表具体某个类的代理类生成的全部信息
@Override public Set getSupportedAnnotationTypes() {
Set types = new LinkedHashSet<>();
types.add(BindArray.class.getCanonicalName());
types.add(BindBitmap.class.getCanonicalName());
types.add(BindBool.class.getCanonicalName());
types.add(BindColor.class.getCanonicalName());
types.add(BindDimen.class.getCanonicalName());
types.add(BindDrawable.class.getCanonicalName());
types.add(BindInt.class.getCanonicalName());
types.add(BindString.class.getCanonicalName());
types.add(BindView.class.getCanonicalName());
types.add(BindViews.class.getCanonicalName());
for (Class extends Annotation> listener : LISTENERS) {
types.add(listener.getCanonicalName());
}
return types;
}
(2) ButterknifeProcessor.process()
这个方法的作用主要是扫描、评估和处理我们程序中的注解,然后生成Java文件。即ViewBinder。
@Override public boolean process(Set extends TypeElement> elements, RoundEnvironment env) {
//下面这一句解析注解
Map targetClassMap = findAndParseTargets(env);
//解析完成以后,需要生成的代码结构已经都有了,它们都存在于每一个 BindingClass 当中
for (Map.Entry entry : targetClassMap.entrySet()) {
TypeElement typeElement = entry.getKey();
BindingClass bindingClass = entry.getValue();
try {
bindingClass.brewJava().writeTo(filer);
} catch (IOException e) {
error(typeElement, "Unable to write view binder for type %s: %s", typeElement,
e.getMessage());
}
}
return true;
}
(2) ButterknifeProcessor.findAndParseTargets()
(1)扫描所有具有注解的类,然后根据这些类的信息生成BindingClass,最后生成以TypeElement为键,BindingClass为值的键值对。
(2)循环遍历这个键值对,根据TypeElement和BindingClass里面的信息生成对应的java类。例如AnnotationActivity生成的类即为Cliass$$ViewBinder类。
private Map findAndParseTargets(RoundEnvironment env) {
Map targetClassMap = new LinkedHashMap<>();
Set erasedTargetNames = new LinkedHashSet<>();
// Process each @BindArray element.
for (Element element : env.getElementsAnnotatedWith(BindArray.class)) {
if (!SuperficialValidation.validateElement(element)) continue;
try {
parseResourceArray(element, targetClassMap, erasedTargetNames);
} catch (Exception e) {
logParsingError(element, BindArray.class, e);
}
}
// Process each @BindBitmap element.
for (Element element : env.getElementsAnnotatedWith(BindBitmap.class)) {
if (!SuperficialValidation.validateElement(element)) continue;
try {
parseResourceBitmap(element, targetClassMap, erasedTargetNames);
} catch (Exception e) {
logParsingError(element, BindBitmap.class, e);
}
}
......
四:Javapoet:生成Java代码
(1)以代码拼接的方式
(2)TypeSpec代表类,MethodSpec代表方法(Builder模式)
TypeSpec.Builder result = TypeSpec.classBuilder(className)
//添加修饰符为 public,生成的类是 public 的
.addModifiers(PUBLIC)
.addTypeVariable(TypeVariableName.get("T", ClassName.bestGuess(targetClass)));
MethodSpec.Builder result = MethodSpec.methodBuilder("bind")
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(FINDER, "finder", FINAL)
.addParameter(TypeVariableName.get("T"), "target", FINAL)
.addParameter(Object.class, "source");
五:ButterKnife模块
ioc-annotation 用于存放注解等,Java模块
ioc-compiler 用于编写注解处理器,Java模块
ioc-api 用于给用户提供使用的API,本例为Andriod模块
ioc-sample 示例,本例为Andriod模块
六:解析注解的流程(收集注解的流程)
(1)roundEnv.getElementsAnnotatedWith(BindView.class)拿到所有@BindView注解的成员变量
(2)循环每一个成员变量,进行检查。
(3)获取该成员变量的Element对应得类TypeElement及全路径名.
(4)创建类(proxyInfo)封装一个类的注解信息。
(4)把该成员变量对应的注解存入proxyInfo(key:id,value:variableElement)
private Map mProxyMap = new HashMap();
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv){
mProxyMap.clear();
Set extends Element> elements = roundEnv.getElementsAnnotatedWith(BindView.class);
//一、收集信息
for (Element element : elements){
//检查element类型
if (!checkAnnotationUseValid(element)){
return false;
}
//field type
VariableElement variableElement = (VariableElement) element;
//class type
TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();//TypeElement
String qualifiedName = typeElement.getQualifiedName().toString();
ProxyInfo proxyInfo = mProxyMap.get(qualifiedName);
if (proxyInfo == null){
proxyInfo = new ProxyInfo(mElementUtils, typeElement);
mProxyMap.put(qualifiedName, proxyInfo);
}
BindView annotation = variableElement.getAnnotation(BindView.class);
int id = annotation.value();
proxyInfo.mInjectElements.put(id, variableElement);
}
return true;
}