# View Binding 大致原理简易分析


# View Binding 大致原理简易分析

## 1. 简易介绍

ViewBinding 是 Android 中的一种视图绑定的工具，可以给 layout xml 动态创建属于它的 Layout Binding Class，不过我们应该关注一下这个东西是怎么实现的，我们要分为编译前和编译后：

- 编译前：
  1. KAE: 分析 XML 文件，通过 IDE 插件(Kotlin) SyntheticResolveExtension 创建 Synthetic 语句(替换为 findViewById)
  2. ViewBinding: 分析 XML 文件，通过 IDE 插件(Android) shortNamesCache，customUsageSearcher，resolveScopeEnlarger 等来实现实时创建 Layout Binding Class
- 编译后：
  1. KAE: 通过 kotlin compiler plugin，修改 JVM ByteCode/IR，编织入类似 findViewById 这种代码，本质上和 kotlin data class 的 copy 方法的实现原理是类似的
  2. ViewBinding: 生成 Binding 类，并于项目中其他代码一同参与编译

所以其实 ViewBinding 是要比 KAE 更加低效的，当然肯定都不如直接写 findViewById 效率高（

### 简易分析

通过 ViewBinding(以下简称 VB) 的创建方式涉及到的类我们大致可以猜测这东西是位于 DataBinding repo 的，VB 只是用到了其中一部分功能，因此我们需要找到 DataBinding(以下简称 DB) 的源码，由于 DB/VB 在引入时需要我们在 `build.gradle` 中进行配置：

```groovy
android {
    viewBinding {
        enabled = true
    }
}
```

因此我们就可以去看看 AGP 的 repo：

首先我们可以去 [Maven Repository: Search/Browse/Explore (mvnrepository.com)](https://mvnrepository.com/)，找到 AGP，我们也知道 VB 是在 [AGP 3.6](https://mvnrepository.com/artifact/com.android.tools.build/gradle/3.6.0) 之后的版本引入的，于是可以在其依赖类里面发现：![](https://cdn.jsdelivr.net/gh/zsqw123/cdn@master/picCDN/screenshot-20220320-140136.png)

第一个就是 DB，同时查看这个东西的反向依赖也能发现其 compiler， 我们发现这是个注解处理器，会在编译时动态生成 class![](https://cdn.jsdelivr.net/gh/zsqw123/cdn@master/picCDN/screenshot-20220320-140934.png)

这些都是给编译器用的，如果要做到实时提示的话，要依赖 IDE 的支持，我们也可以找到与 AGP 搭配食用的 IDEA Android Plugin: [JetBrains/android: Android Plugin for IntelliJ IDEA (github.com)](https://github.com/JetBrains/android)，不过项目是使用 Google 内部比较常用的 bazel 进行构建的，而不是 Gradle，这里我们可以对项目进行一些改造方便我们在 IDEA 中查看源码以及引用跳转，我这里有一份自己的改造：[zsqw123/DataBinding-IDEA-Plugin](https://github.com/zsqw123/DataBinding-IDEA-Plugin)，可以直接在 IDEA 中打开和智能提示



## 2. IDE Plugin 分析

对于任何一个 IDEA Plugin，我们都可以在 META-INF 中找到插件配置 `.xml` 文件，在 DB 的 `.xml` 中，我筛选了几个 ViewBinding 主要用到的这些扩展，每一个扩展事实上都是定义了一个接口/抽象类，我们实现相应的扩展就是去实现指定的接口并在这里进行注册。

```xml
<java.shortNamesCache implementation="..LayoutBindingShortNamesCache"/>
<resolveScopeEnlarger implementation="..BindingScopeEnlarger" />
<java.elementFinder implementation="..BindingClassFinder" />
<moduleService serviceImplementation="..LayoutBindingModuleCache" />

<customUsageSearcher implementation="..DataBindingResourceUsageSearcher"/>
<automaticRenamerFactory implementation="..DataBindingRenamerFactory"/>
```

这几个扩展的大致流程是：

接下来我们就来说说这几个扩展的作用：

### shortNamesCache

用于 Code Completion，在输入非全类名时，提示出项目中的文件以及 Class，Method，Field 等，并进行 suggesting import

### resolveScopeEnlarger

虚拟生成的 class 存在于 Light VirtualFileSystem 中，并不是项目源码所在的位置，如果我们要让 IDE 找到这些虚拟生成的 class，我们就需要通过 `ResolveScopeEnlarger`和`KotlinResolveScopeEnlarger` 实现

### elementFinder

这个扩展可以实现按 qualifiedName 定位类或包，在 VB 中，`activity_main.xml` 将会找到其生成的类 `ActivityMainBinding`

### moduleService

针对 Module 注册的服务，在 VB 中，在 DB 中的这个 `LayoutBindingModuleCache` 中，通过 `getLightBindingClasses` 真正生成了 `LightBindingClass`

### 其他

- customUsageSearcher 用来找到 Usage 引用，在重构时方便找到关联的引用
- automaticRenamerFactory 用于重构，方便批量重命名引用



## 3. 参考

- [android light classes](./220318-android-plugin)
- [kotlin light classes](./220318-kotlin-plugin)
