神刀安全网

源码解析:ButterKnife(7.0.1)

看过很多的源码解析的文章,都是从整体的框架设计入手开讲的,对于我们这种菜鸟而言,无异于天书,一点也不接地气,格式例如:android-Ultra-Pull-To-Refresh 源码解析,可能你看了半天,感觉似懂非懂了,但是让你去讲这个框架的一些实现细节,你可能还是一脸懵逼。

这里了,我使用了另一种更适合我们菜鸟的阅读源码的方式:从实际使用入手,跟踪具体流程

预备知识:

Java反射
Java注解

1.ButterKnife的使用


源码解析:ButterKnife(7.0.1)

2.源码解析:


我们使用ButterKnife时,使用了@Bind和@OnClick这两个注解,还有ButterKnife.bind(this)这一个初始化的方法。OK,就从它们开始我们的解析之路吧:

温馨提示:本次解析的流程可能比较绕,请打开IDE,依赖ButterKnife,一边观看本博客,一边查看ButterKnife的源码!(解析版本:7.0.1)

(1)绑定视图id

源码解析:ButterKnife(7.0.1)

(2)绑定点击事件

源码解析:ButterKnife(7.0.1)

OnClick类第28行,调用了另一个自定义注解

源码解析:ButterKnife(7.0.1)

OnClick类第32行,也调用了另一个自定义注解

源码解析:ButterKnife(7.0.1)

(3)在activity.onCreate中初始化时

源码解析:ButterKnife(7.0.1)

源码解析:ButterKnife(7.0.1)

首先,bind方法的第317行,会调用findViewBinderForClass,获取ViewBinder对象

源码解析:ButterKnife(7.0.1)

然后,bind方法的第319行,会调用ViewBinder对象的bind方法

源码解析:ButterKnife(7.0.1)

坑爹啊,居然是接口,那么,它的实现类在哪呢?暂且不表,下回细说。
先深入findViewBinderForClass方法

源码解析:ButterKnife(7.0.1)

第339、340行,会用反射创建一个ViewBinder对象,类名为:原类名+

源码解析:ButterKnife(7.0.1)

这个玩意应该就是ViewBinder的实现类了,但是我找遍ButterKnife的源码,都找不到该实现类的源码。
于是,源码解析卡在这里了。后来,看了AbstractProcessor的相关知识,才知道,注解可以分为:运行时注解、编译时注解,
运行时注解就是就是运行时运用反射,动态获取对象、属性、方法等,一般的IOC框架就是这样,可能会牺牲一点效率。
然而,大名鼎鼎的ButterKnife运用的是编译时注解。
编译时注解就是在程序编译时根据注解进行一些额外的操作,ButterKnife在我们编译时,就根据注解,自动生成了一些辅助类。
入口为AbstractProcessor的process方法。好,终于又有方向,继续深入。

(4)编译时注解**,ButterKnifeProcessor会自动根据注解生成辅助类**
在源码中找到了AbstractProcessor的子类:ButterKnifeProcessor

源码解析:ButterKnife(7.0.1)

有点坑爹,源码中AbstractProcessor等类会标红,好奇怪,我明明程序可以好好的运行呀,怎么会找不到这些类呢?javax不是jdk自带的api吗?
算了,先不管这了,继续看。

源码解析:ButterKnife(7.0.1)

其中,process方法的129行,调用了brewJava方法,哈哈,就是这个方法,会自动创建java文件并写入代码

源码解析:ButterKnife(7.0.1)

我将程序编译后,在

源码解析:ButterKnife(7.0.1)

目录下找到了自动创建的java文件,如下:
看这个类的名称,“$$ViewBinder”,是不是有印象。嘿嘿,ViewBinder的实现类,终于找到了。

源码解析:ButterKnife(7.0.1)

第11行,调用findRequiredView方法

源码解析:ButterKnife(7.0.1)

源码解析:ButterKnife(7.0.1)

首先,调用findView,找到View类型的目标对象
坑爹啊,抽象方法,那么实现方法在哪呢?再次短路!

源码解析:ButterKnife(7.0.1)

好吧,暂时略过,先去看看转换View类型的方法,发现其实就是强转而已

源码解析:ButterKnife(7.0.1)

继续寻找findView的实现类,全局搜索“findView(”,然后发现:

源码解析:ButterKnife(7.0.1)

上面的抽象方法findView其实也在枚举类Finder中,好吧,原来枚举类中可以定义抽象方法,而且,枚举值,还可以实现它的抽象方法,表示又学到了一招。
我们传入的为Activity,于是,见第100行,哈哈,熟悉的findViewById终于看到了。好!ButterKnife的@bind注解的流程已经走通了,下面再看@OnClick的流程:

返回去看那个自动生成的$$ViewBinder类,

源码解析:ButterKnife(7.0.1)

见第17行,DebouncingOnClickListener即为点击事件的监听器,

源码解析:ButterKnife(7.0.1)

第26行,在onClick方法中,调用了抽象方法doClick,实现方法在哪呢?在自动生成的辅助类中,见$$ViewBinder类的第18行。

源码解析:ButterKnife(7.0.1)

实现方法中,调用了target.doMyClick(po),嘿嘿,回去看我们的使用类LoginActivity,见第44行,好!ButterKnife的@bind注解的流程也走通了!

源码解析:ButterKnife(7.0.1)

那么,到这就大功告成了吗?不!
现在我们只知道,ButterKnife在编译时,根据注解,自动生成了一个辅助类,这个辅助类,帮我们搞定了findViewById和OnClick!
但是,这个辅助类的生成细节,我们还不是很清楚。

(5)分析**ButterKnifeProcessor自动生成代码的细节**

通过前面的分析,我们知道,自动生成主要涉及到两个方法:
入口方法process
写码方法brewJava

再次回顾下最终生成的辅助类:

源码解析:ButterKnife(7.0.1)

先看与这个类联系最紧密的brewJava方法吧:

源码解析:ButterKnife(7.0.1)

包名、导入类、类名的生成,都可以一目了然,问题是:bind和unbind方法里面的细节,这些都是和我们自己写的代码紧密联系的,它是怎么知道我们的字段名和方法名的?
不得不说,反射和注解真是太牛逼了,也许有一天,真的就可以完全用机器写代码了。好了,先不感慨了,继续看代码:
第104行,调用emitBindMethod方法
第106行,调用emitUnbindMethod方法

源码解析:ButterKnife(7.0.1)

第127行,遍历id,调用emitViewBindings方法

源码解析:ButterKnife(7.0.1)

第198行,看到了辅助类的11行的东东,然后调用了emitHumanDescription方法

源码解析:ButterKnife(7.0.1)

其实这个方法没什么用,看看就过去吧。

再看process:

源码解析:ButterKnife(7.0.1)

第120行,调用findAndParseTargets方法,查找并解析目标。
不得不说,大牛的代码真的是不用太多的注释的,代码本身就是注释了,实乃吾辈学习楷模!

源码解析:ButterKnife(7.0.1)

源码解析:ButterKnife(7.0.1)

其实就是根据不同的注解,分别遍历,这里我们只分析@Bind和@OnClick,所以就只看第148行的parseBind和第156行的findandParseListener了,

源码解析:ButterKnife(7.0.1)

先看parseBind,因为我们的使用类中@Bind的参数只是一个id,这里就只看parseBindOne

啊,快绕糊涂了,先略过吧,大家可以顺着我的思路继续去探究……

参考目录:


  1. Annotation实战【自定义AbstractProcessor】
  2. Android 打造编译时注解解析框架 这只是一个开始

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » 源码解析:ButterKnife(7.0.1)

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址