BufferKinfe绑定View的原理分析

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




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值