Butterknife源码详解
2019-01-25 本文已影响0人
kjy_112233
一、源码解析
(1)ButterKnife.bind(this)
@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
//通过target获取当前xml布局
View sourceView = target.getWindow().getDecorView();
return bind(target, sourceView);
}
@NonNull @UiThread
public static Unbinder bind(@NonNull Object target, @NonNull View source) {
Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
//通过class查找constructor即上文生成的classname_ViewBinding类
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
try {
//调用newInstance构造方法构造一个Unbinder
return constructor.newInstance(target, source);
}
//code...
}
//缓存集合
@VisibleForTesting
static final Map<Class<?>, Constructor<? extends Unbinder>> BINDINGS = new LinkedHashMap<>();
@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
//缓存的LinkedHashMap 查找当前的activity是否缓存,BINDINGS是绑定的缓存
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
if (bindingCtor != null || BINDINGS.containsKey(cls)) {
//如果有缓存直接返回
return bindingCtor;
}
String clsName = cls.getName();
//code...
try {
//通过反射去加载clsName + "_ViewBinding"这个类
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
//noinspection unchecked
bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
}
//code...
//获取到实例将它加入BINDINGS缓存并返回,建立缓存,会尽量减少性能损失
BINDINGS.put(cls, bindingCtor);
return bindingCtor;
}
(2)看看生成的MainActivity_ViewBinding.java
public class MainActivity_ViewBinding implements Unbinder {
private MainActivity target;
private View view7f080046;
@UiThread
public MainActivity_ViewBinding(MainActivity target) {
this(target, target.getWindow().getDecorView());
}
@UiThread
public MainActivity_ViewBinding(final MainActivity target, View source) {
this.target = target;
View view;
//findViewById
target.textView = Utils.findRequiredViewAsType(source, R.id.textView, "field 'textView'", TextView.class);
view = Utils.findRequiredView(source, R.id.button, "field 'button' and method 'onButton'");
target.button = Utils.castView(view, R.id.button, "field 'button'", Button.class);
view7f080046 = view;
//点击事件
view.setOnClickListener(new DebouncingOnClickListener() {
@Override
public void doClick(View p0) {
target.onButton();
}
});
target.imageView = Utils.findRequiredViewAsType(source, R.id.imageView, "field 'imageView'", ImageView.class);
//资源引用
Context context = source.getContext();
Resources res = context.getResources();
target.message = res.getString(R.string.name);
}
@Override
@CallSuper
public void unbind() {
MainActivity target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
this.target = null;
target.textView = null;
target.button = null;
target.imageView = null;
view7f080046.setOnClickListener(null);
view7f080046 = null;
}
}
- MainActivity_ViewBinding解析
public static <T> T findOptionalViewAsType(View source, @IdRes int id, String who,
Class<T> cls) {
View view = source.findViewById(id);
return castView(view, id, who, cls);
}
public static View findRequiredView(View source, @IdRes int id, String who) {
View view = source.findViewById(id);
if (view != null) {
return view;
}
//code...
}
public static <T> T castView(View view, @IdRes int id, String who, Class<T> cls) {
try {
//强制转换为控件类型
return cls.cast(view);
}
//code...
}
(3)ButterKnifeProcessor解析
- ButterKnife中使用了注解的解析处理器AbstractProcessor;AbstractProcessor是一个抽象类,该类实现了接口Processor;ButterKnife的注解处理器叫ButterKnifeProcessor
- init
@Override
public synchronized void init(ProcessingEnvironment env) {
super.init(env);
//code...
//用来处理TypeMirror的工具类
typeUtils = env.getTypeUtils();
//用来创建生成辅助文件的工具类
filer = env.getFiler();
//code...
}
- getSupportedAnnotationTypes
@Override
public Set<String> getSupportedAnnotationTypes() {
//创建了一个LinkedHashSet对象
Set<String> types = new LinkedHashSet<>();
//将getSupportedAnnotations()方法返回的支持注解集合进行遍历添加到types中返回
for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {
types.add(annotation.getCanonicalName());
}
return types;
}
- getSupportedSourceVersion:注册了一系列的Bindxxx注解类和监听列表LISTENERS
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;
}
private static final List<Class<? extends Annotation>> LISTENERS = Arrays.asList(
OnCheckedChanged.class,
OnClick.class,
OnEditorAction.class,
OnFocusChange.class,
OnItemClick.class,
OnItemLongClick.class,
OnItemSelected.class,
OnLongClick.class,
OnPageChange.class,
OnTextChanged.class,
OnTouch.class
);
- process
@Override
public boolean process(Set<? extends TypeElement> elements, RoundEnvironment env) {
//查找解析绑定元素:TypeElement:类、接口、Fragment;BindingSet:类绑定集合
Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(env);
//遍历解析生成java
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)生成了java源文件
javaFile.writeTo(filer);
} catch (IOException e) {
error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());
}
}
return false;
}
- findAndParseTargets(env)
private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();
Set<TypeElement> erasedTargetNames = new LinkedHashSet<>();
//for循环code...
//一系列处理每一个@Bindxxx元素的for循环代码块
for (Element element : env.getElementsAnnotatedWith(BindViews.class)) {
try {
parseBindViews(element, builderMap, erasedTargetNames);
} catch (Exception e) {
logParsingError(element, BindViews.class, e);
}
}
for (Class<? extends Annotation> listener : LISTENERS) {
findAndParseListener(env, listener, builderMap, erasedTargetNames);
}
Map<TypeElement, ClasspathBindingSet> classpathBindings =
findAllSupertypeBindings(builderMap, erasedTargetNames);
//将builderMap中的数据添加到队列中
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();
//获取集合中的key和value
TypeElement type = entry.getKey();
BindingSet.Builder builder = entry.getValue();
//查找当前类元素的父类元素
TypeElement parentType = findParentType(type, erasedTargetNames, classpathBindings.keySet());
//判断是否存在父类元素,不存在直接构建builder放入bindingMap中;
//存在将parentBinding添加到BindingSet.Builder这个建造者对象,然后创建BindingSet再添加到bindingMap中
if (parentType == null) {
bindingMap.put(type, builder.build());
} else {
BindingSet.Builder parentBinding = bindingMap.get(parentType);
if (parentBinding == null) {
parentBinding = classpathBindings.get(parentType);
}
if (parentBinding != null) {
builder.setParent(parentBinding);
bindingMap.put(type, builder.build());
} else {
entries.addLast(entry);
}
}
}
return bindingMap;
}
- binding.brewJava(sdk, debuggable)
JavaFile brewJava(int sdk, boolean debuggable) {
//bindingConfiguration保存了所有的绑定配置信息
TypeSpec bindingConfiguration = createType(sdk, debuggable);
//bindingConfiguration对象构建生成一个JavaFile对象
return JavaFile.builder(bindingClassName.packageName(), bindingConfiguration)
.addFileComment("Generated code from Butter Knife. Do not modify!")
.build();
}
- 在编译的时候扫描注解,并通过自定义的ButterKnifeProcessor做相应的处理解析得到bindingMap对象,最后调用 javapoet 库生成 java模板代码。
- 当我们调用ButterKnife的bind()方法的时候,它会根据类的全限定类型,找到相应的模板代码,并在其中完成findViewById、setOnClick、setOnLongClick等操作。
- ButterKnife不能将控件和方法设置为private或者static,是因为在className_ViewBinder类会直接调用该控件和方法进行赋值。
- 使用编译时注解,在编译时期自动生成Unbinder(APT),这个对象自动生成了我们需要的动画、监听事件等等。