转载请注明:http://blog.csdn.net/chenkai19920410/article/details/51020151
ButterKnife是Jake Wharton大神写开源框架。项目托管地址:
https://github.com/JakeWharton/butterknife。
相信不少人已经非常熟悉他的使用了。网上有很多介绍其使用的文章。本文主要是想介绍一下,ButterKnife的实现原理。在阅读本文之前,可能需要先对Java注解器Annotation Processor有所了解。
推荐文章:
原版文章
翻译版
读完该文相信可以对Java Annotation Processor有了比较深入的了解。
我们知道Spring的注解是使用Java反射机制实现的,当然如果让我们实现注解的话,可能往往也是想到利用反射来实现。但是我们知道如果通过反射,是在运行时(Runtime)来处理View的绑定等一些列事件的,这样比较耗费资源,会影响应用的性能。所以ButterKnife利用的是上文中提到的Java Annotation Processor技术,自定义了我们平时常用的一些注解,并注册相应的注解处理器,最后生成了相应的辅助类。在编译时直接通过辅助类来完成操作,这样就不会产生过多的消耗,而出现性能问题。接下来我们就一步步分析BufferKnife具体的实现。
自定义的注解类
ButterKnife自定义了很多我们常用的注解,@Bind,@OnClick等。看源码
/**
* Bind a field to the view for the specified ID. The view will automatically be cast to the field
* type.
* <pre><code>
* {@literal @}Bind(R.id.title) TextView title;
* </code></pre>
*/
@Retention(CLASS) @Target(FIELD)
public @interface Bind {
/** View ID to which the field will be bound. */
@IdRes int[] value();
}
通过@Target(FIELD)可以看出,该注解是使用在成员变量上的,同样的我们在看看@OnClick注解的声明:
/**
* Bind a method to an {@link OnClickListener OnClickListener} on the view for each ID specified.
* <pre><code>
* {@literal @}OnClick(R.id.example) void onClick() {
* Toast.makeText(this, "Clicked!", Toast.LENGTH_SHORT).show();
* }
* </code></pre>
* Any number of parameters from
* {@link OnClickListener#onClick(android.view.View) onClick} may be used on the
* method.
*
* @see OnClickListener
*/
@Target(METHOD)
@Retention(CLASS)
@ListenerClass(
targetType = "android.view.View",
setter = "setOnClickListener",
type = "butterknife.internal.DebouncingOnClickListener",
method = @ListenerMethod(
name = "doClick",
parameters = "android.view.View"
)
)
public @interface OnClick {
/** View IDs to which the method will be bound. */
@IdRes int[] value() default { View.NO_ID };
}
注解对象时方法Method,这和我们平时使用方式是吻合的。还有其他的注解,可以在源码butterknife-annotations.butterknife包下查看。
ButterknifeProcessor
有了这些注解,那么还必须实现注解处理器,ButterKnife的注解处理器是ButterKnifeProcessor类,如果有读过推荐的文章就会知道该类是继承自AbstractProcessor的。这里先简单的介绍一下AbstractProcessor。
自定义的Processor都必须继承自AbstractProcessor,并重写process方法,不过我们往往还会重写其他的方法,如下:
public class TProcessor extends AbstractProcessor{
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
return false;
}
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return super.getSupportedAnnotationTypes();
}
@Override
public SourceVersion getSupportedSourceVersion() {
return super.getSupportedSourceVersion();
}
}
同样的ButterKnifeProcessor主要也是实现了这几个方法。我们具体来看看:
private Elements elementUtils;
private Types typeUtils;
private Filer filer;
@Override public synchronized void init(ProcessingEnvironment env) {
super.init(env);
elementUtils = env.getElementUtils();
typeUtils = env.getTypeUtils();
filer = env.getFiler();
}
@Override public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new LinkedHashSet<>();
types.add(Bind.class.getCanonicalName());
for (Class<? extends Annotation> listener : LISTENERS) {
types.add(listener.getCanonicalName());
}
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(Unbinder