项目需求讨论— ButterKnife初级小结

前言

在没有使用DataBinding之前,我的项目都是使用ButterKnife,当然对于ButterKnife大家估计都熟悉的不要太熟悉了。本文我也就当自己的一个总结。

基本使用

基本使用我就直接贴相关的ButterKnife使用教程的文章链接:

ButterKnife官方教程的翻译

butterknife github链接

从Github链接我们也可以看出有哪些基本的功能注解:

butterknife : android library model 提供android使用的API
butterknife-annotations: java-model,使用时的注解
butterknife-compiler: java-model,编译时用到的注解的处理器
butterknife-gradle-plugin: 自定义的gradle插件,辅助生成有关代码
butterknife-integration-test: 该项目的测试用例
butterknife-lint: 该项目的lint检查
sample: demo

我们同时可以在butterknife-annotations里面看到基本的注解:

具体流程

我们从二个方面来看:

1.生成XXXX_ViewBinding.java文件:

public class AAA extends Activity{

    @BindView(R.id.textView)
    TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.aaa);
    }
}

我们写一个最简单的Demo,就绑定了一个TextView。(这时候并没有ButterKnife.bind(this)代码) 我们按下Android Studio的编译按钮,当编译完成后,我们查看文件:

我们可以发现在编译后多了一个AAA_ViewBinding.java文件

public class AAA_ViewBinding implements Unbinder {
  private AAA target;//可以看到这里的target是AAA这个Activity的实例对象。

  @UiThread
  public AAA_ViewBinding(AAA target) {
    //先调用这个一个参数的构造函数,然后调用下面的二个参数的构造函数
    this(target, target.getWindow().getDecorView());
  }

  @UiThread
  public AAA_ViewBinding(AAA target, View source) {
  
    //这里的target就是AAA这个Activity的实例,
    //source就是上面的单个构造函数生成的AAA这个Activity的DecorView
    /*(ps:一般我们在Activity中setContentView(R.layout.XXX);
          就是在我们写的布局设置到了DecorView里面。)
    */
    
    this.target = target;
    
    //target.tv就是我们写的AAA里面定义的TextView tv;
    //通过Utils.findRequiredViewAsType进行tv的实例化,
    /*(ps:因为这里是要taget.tv,所以如果我们在AAA里面的TextView用的private就会有问题了
        当然实际private的判断是在生成AAA_ViewBinding.java这个文件的时候就会去判断
    )*/

    target.tv = Utils.findRequiredViewAsType(source, R.id.textView, "field 'tv'", TextView.class);
  }

  @Override
  @CallSuper
  public void unbind() {
    AAA target = this.target;
    if (target == null) throw new IllegalStateException("Bindings already cleared.");
    this.target = null;

    target.tv = null;
  }
}

我们可以具体看下Utils.findRequiredViewAsType方法:

public static <T> T findRequiredViewAsType(View source, @IdRes int id, String who,
      Class<T> cls) {
    View view = findRequiredView(source, id, who);
    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;
    }
    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);来帮我们TextView赋值。

可能大家会说。那这个AAA_ViewBinding.java文件到底是怎么生成的,为什么编译下后,会生成这么个文件,为什么这个文件里面相关代码也会有等。(初步可以这么理解,就是遍历了我们在AAA.java中写的注解,然后按照相应模板生成一个java文件。)

详细的生成AAA_ViewBinding.java文件可以看以下链接:

深入理解ButterKnife源码并掌握原理(一)

深入理解ButterKnife源码并掌握原理(二)

深入理解ButterKnife源码并掌握原理(三)

2.实例化XXX_ViewBinding.java:

我们在上面的例子中可以看到,在生成的AAA_ViewBinding.java中有了对我们的TextView进行赋值。

那么现在的问题就是变成了实例化AAA_ViewBinding.java,然后运行相应的这个TextView赋值的相关代码.

这时候就需要执行ButterKnife.bind(this);(ps:因为我们这个例子是在Activity中)。我们来看ButterKnife.bind(this)到底执行了什么:

ButterKnife -- > bind:

@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
   //target是我们传入的this,也就是AAA这个Activity的实例
   //获取到AAA的DecorView实例化对象sourceView
   View sourceView = target.getWindow().getDecorView();
   //调用createBinding方法
   return createBinding(target, sourceView);
}

createBinding:

private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {

    //1.获取AAA的Class
    Class<?> targetClass = target.getClass();
    if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
    
    //2.通过findBindingConstructorForClass获得构造器
    Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);

    if (constructor == null) {
      return Unbinder.EMPTY;
    }

    //noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
    try {
    
      //3. 通过构造器实例化
      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);
    }
  }

我们发现主要是第二步,获取构造器,所以我们来看下findBindingConstructorForClass方法:

@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 {
    
        //加载了(clsName + "_ViewBinding")的类,也就是AAA_ViewBinding的Class对象,
        //然后在用这个AAA_ViewBinding的Class对象获取构造器。
        
      Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
      //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());
    } catch (NoSuchMethodException e) {
      throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
    }
    BINDINGS.put(cls, bindingCtor);
    return bindingCtor;
  }

所以当执行了ButterKnife最后就是通过构造函数获取了AAA_ViewBinding的实例,而在AAA_ViewBinding.java的构造函数中对TextView进行了赋值。

结尾

如果哪里有问题,欢迎大家留言提出。我可以及时改正,()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345