BufferKinfe绑定View的原理分析

xiaoxiao2021-02-28  41

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,但是这里通过自动化生成的类来处理之后,就省去了我们很多的时间

转载请注明原文地址: https://www.6miu.com/read-1150006.html

最新回复(0)