Butterknife简介
Description:Field and method binding for Android views which uses annotation processing to generate boilerplate code for you.(GitHub)
ButterKnife是JakeWharton出品的一个专注于Android系统的View注入框架,以前总是要写很多findViewById来找到View对象,有了ButterKnife可以很轻松的省去这些步骤。最重要的一点,使用ButterKnife对性能基本没有损失,因为ButterKnife用到的注解并不是在运行时反射的,而是在编译的时候生成新的class。项目集成起来也是特别方便,使用起来也是特别简单。GitHub:github.com/JakeWharton…。
Butterknife 简单分析
当你编译你的Android工程时,ButterKnife工程中ButterKnifeProcessor类的process()方法会执行以下操作: 开始它会扫描Java代码中所有的ButterKnife注解@Bind、@OnClick、@OnItemClicked等 当它发现一个类中含有任何一个注解时,ButterKnifeProcessor会帮你生成一个Java类,名字类似$$ViewBinder,这个新生成的类实现了ViewBinder接口。 这个ViewBinder类中包含了所有对应的代码,比如@Bind注解对应findViewById(), @OnClick对应了view.setOnClickListener()等等。 最后当Activity启动ButterKnife.bind(this)执行时,ButterKnife会去加载对应的ViewBinder类调用它们的bind()方法
以bind(Activity)代码为例
public static Unbinder bind(@NonNull Activity target) {
View sourceView = target.getWindow().getDecorView();
return bind(target, sourceView);
}
方法中通过传入的Activity获取DecorView,DecorView是整个View树的顶层View,内部包含标题栏和ContentView,而ContentView内部就是我们定义的视图View,拿到DecorView后调用用createBinding方法并把目标Activity和DecorView传入过去
@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());
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
return constructor.newInstance(target, source);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InstantiationException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException("Unable to create binding instance.", cause);
}
}
方法中获取目标Class,并通过findBindingConstructorForClass方法获取构造函数。加载当前的class的名字加上_ViewBinding,比如MainActivity_ViewBinding,加载并获取该class,在获取它的两个参数的构造函数,最后将加载的class进行缓存存入BINDINGS集合中。之后通过createBinding方法中的newInstance进行实例化。
从上面Butterknife执行的绑定方法就可以知道先去加载classname+_ViewBinding的类,并进行实例化,但这个类我们并没有编写,是自动生成的,也就是编译时生成,编译时注解时需要AbstractProcessor这个类来实现,需要重写它的process方法。
那么这个ClassName_ViewBinding类是如何生成的呢?
涉及AnnotationProcessor技术 和 APT(Annotation Processing Tool)技术,它是一种注解处理器,项目编译时对源代码进行扫描,找出存活时间为 RetentionPolicy.CLASS 的指定注解,然后对注解进行解析处理,进而得到要生成类的必要信息,根据这些信息动态生成对应的Java类(也就是ClassName_ViewBinding)。
注意:@AutoService(Processor.class)注解,来实现注解处理器的注册,注册到javac后,在项目编译时就能执行注解处理器了。
去除Butterknife 为何?
GitHub 上 README 首先就是 Attention:This tool is now deprecated. Please switch to view binding. Existing versions will continue to work, obviously, but only critical bug fixes for integration with AGP will be considered. Feature development and general bug fixes have stopped.(现在不推荐使用此工具。请切换到查看绑定。很明显,现有版本将继续起作用,但仅考虑与AGP集成的关键错误修复程序。功能开发和常规错误修复已停止。)
官方建议这么做了,那就做了吧哈哈~~~
我们项目是有ViewBinding 、DataBinding、kotlin-android-extensions 和 Butterknife 四种视图绑定框架,前二者是官方开启就行。
buildFeatures {
// for view binding :
viewBinding = true
}
当我们通过Id去找控件的时候,你会发现一大堆同名的控件,这就会导致不能快速定位控件,影响开发效率。
个人心得:layout /layout 是DataBinding 专属的,建议使用viewBinding。不要在XML做数据绑定操作