Buffer Kinfe大家都很熟悉,用起来也很方便,那么BufferKinfe是怎么样一个实现的原理呢?我看了一下一些文章之后,理解了一下
首先是第一块,如何使用
一、环境配置
首先需要在项目的Project的build.gradle中配置
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
这样配置就变成了
// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:2.3.2' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'//添加这行 } } allprojects { repositories { jcenter() } } task clean(type: Delete) { delete rootProject.buildDir }
然后是在所要用的Module中配置
apply plugin: 'com.neenbedankt.android-apt' //添加这行
dependencies {
compile 'com.jakewharton:butterknife:8.6.0'//添加这行
apt 'com.jakewharton:butterknife-compiler:8.6.0'//添加这行
}
这样就完成了基本使用的配置,我已经将多余的与ButterKinfe无关的配置都不在这里展示了
二、使用以及分析
Buffer Kinfe主要是使用了APT(Annotation Processing Tool)编译时解析技术,就是在编译的时候生成一些辅助类,来完成一些功能的依赖实现。
在这里如何使用我就不特地介绍了,直接开始看原理
通过查看部分源码是可以看到
public class MainActivity extends AppCompatActivity {
@BindView(R.id.text)
TextView text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
text.setText("我找到我了");
}
}
我们从ButterKinfe.bind(this)进去看看
@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
View sourceView = target.getWindow().getDecorView();
return createBinding(target, sourceView);
}
进去看createBinding,从传入的参数来看,是通过Activity和根视图来创建绑定的类
private static Unbinder createBinding(@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;
}
在创建的同时,这里返回了一个用于解绑的对象
@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);//从缓存中取,避免重复创建
if (bindingCtor != null) {
if (debug) Log.d(TAG, "HIT: Cached in binding map.");
return bindingCtor;
}
String clsName = cls.getName();
if (clsName.startsWith("android.") || clsName.startsWith("java.")) {//当前的类是系统类的时候就没必要再创建了,增加效率
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return null;
}
try {
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");//加载辅助的binding类,反射
//noinspection unchecked
bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
if (debug) Log.d(TAG, "HIT: Loaded binding class and constructor.");
} catch (ClassNotFoundException e) {
if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());
bindingCtor = findBindingConstructorForClass(cls.getSuperclass());//从父类中取,到了系统类就没有再往上一层的必要了,这里就是上面android.的原因
} catch (NoSuchMethodException e) {
throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
}
BINDINGS.put(cls, bindingCtor);//将类缓存起来
return bindingCtor;
}
再进去
public Constructor<T> getConstructor(Class... parameterTypes) throws NoSuchMethodException, SecurityException {
throw new RuntimeException("Stub!");
}
这里就是解耦部分了,就是上面所说的 使用了 APT生成的类Activity_ViewBinding
那么接下去如何继续走呢,没关系我们在Android Studio里面搜索一下就有了
public class MainActivity_ViewBinding implements Unbinder {
private MainActivity target;
@UiThread
public MainActivity_ViewBinding(MainActivity target) {
this(target, target.getWindow().getDecorView());
}
//这里是在源码中走的路线
@UiThread
public MainActivity_ViewBinding(MainActivity target, View source) {
this.target = target;
//通过id找到对象
target.text = Utils.findRequiredViewAsType(source, R.id.text, "field 'text'", TextView.class);
}
@Override
@CallSuper
public void unbind() {
MainActivity target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
this.target = null;
target.text = null;
}
}
继续看
public static <T> T findRequiredViewAsType(View source, @IdRes int id, String who,
Class<T> cls) {
View view = findRequiredView(source, id, who);//找到View
return castView(view, id, who, cls);//在返回View时强转为需要的类型
}
这里进去找我绑定的TextView
public static View findRequiredView(View source, @IdRes int id, String who) {
View view = source.findViewById(id);
if (view != null) {
return view;
}
String name = getResourceEntryName(source, id);
throw new IllegalStateException("Required view '"
+ name
+ "' with ID "
+ id
+ " for "
+ who
+ " was not found. If this view is optional add '@Nullable' (fields) or '@Optional'"
+ " (methods) annotation.");
}
这里找到就返回,没有找到就抛出异常
可以看到,这里找到我们资源的核心代码
View view = source.findViewById(id);
就是说其实核心的实现方式还是findViewById
三、大致思路
其实总的来说,大体的路线很简单,就是通过APT在编译时生成View绑定的辅助类,通过辅助的类来完成寻找View的过程,如果这些行为由我们来完成,那还不如直接findViewById,但是这里通过自动化生成的类来处理之后,就省去了我们很多的时间