# Databinding引起的 java方法大于 65535 的问题
## 前言
项目中开发使用的是 MVVM 开发模式,并且选择了 Databinding 作为 ViewModel 和 View之间通信的桥梁,再加上一定的封装,使用起来是挺方便的,但是也会遇到一些坑,比如我遇到的这个:
**Databinding 生成的 Java 文件中方法大于 65535 问题。**
如下图所示:
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200305150649.png)
## 缘由
这个问题是怎么来的呢?
其实触发条件挺小的,只有在特定的情况下才会遇到,要满足下面的条件:
1. 使用 Databinding
2. 项目要是中大型的项目(使用 Databinding 的xml文件多)
3. gradle 插件的版本要小于 3.2.0
## 模拟发生
新建项目,然后新建一个 Module:lib2,项目结构如图所示
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200305151101.png)
然后分别在 app 和 lib2 的 build.gradle 中启用 Databinding
```shell
dataBinding{
enabled = true
}
```
设置 gradle 插件版本为 3.1.4. gradle 版本为:4.4
```shell
classpath 'com.android.tools.build:gradle:3.1.4'
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
```
然后创建一个 layout 文件:tag_activity_lib_main.xml
```xml
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingDefaultResource">
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="test" />
```
然后利用脚本把这个 xml 文件拷贝2000份,并分别改名字:
脚本:
```shell
#!/bin/bash
for b in {1..2000}
do
cp tag_activity_lib_main.xml ./tag_activity_lib_main$b.xml
done
```
然后把生成的2000份 xml 文件拷贝到项目的 layout 目录下(app 和 lib2 目录各1000各)。
这个时候应该就会见到这个错误了:
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200305150649.png)
我们看下这个报错的 DatabinderMapperImpl :
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200305151909.png)
> ps:你的可能和我的不一样,xml 文件个数可能不同。
可以看到总共就那几个方法,却有2万多行代码,其中第一个 getDataBinder 方法很长,大概有15368 行,根据错误提示,可以初步断定就是就是这里的方法太大了,超过了 65535 的限制。
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200305163944.png)
可以看到这个 getDataBinder 的作用就是根据传进来的view 的 tag 和 layoutId来返回 和 layoutId 对应的 ViewDataBinding 对象。
即:在 gradle 3.1.4 版本中,会把所有 module 的带有 ...标签的 xml 文件都对应生成一个 ViewDataBinding对象,然后在 DataBinderMapperImpl 中的 getDataBinder 中提供对应的映射关系。
并且会把这个 DataBinderMapperImpl 类放在 app-build-generated-source-apt-android.databinding目录下面。
可以看到:这个 DataBinderMapperImpl 类是在 apt 文件夹下面,也就是这个类是在编译器生成的,这就涉及到了 Android 中的APT 技术,当然这里的编译时生成代码的过程是 google 已经写好的,代码地址在:
[databinding仓库](https://android.googlesource.com/platform/frameworks/data-binding/+/refs/tags/gradle_3.1.2/compiler/src/main/java/android/databinding/annotationprocessor/ProcessDataBinding.java) ,里面是具体的 APT 实现,有兴趣的可以去看看,其实是 google 在 Databinding 库中留下的坑,属于版本问题,好在后面的版本解决了这个问题。
## 解决
其实在 gradle 3.1.4 的下个版本 3.2.0中,google 已经解决这个问题。所以我们只用把 gradle 的版本升级到 3.2.0 以上就能避免这个问题的发生。
当然还有一个办法就是,后面你不再使用 Databinding 来开发,来避免这个问题,但是对于习惯使用 Databinding 的团队来说,这是很难受的,所以还是尽早升级下版本吧。
下面把 gradle 插件版本升级 3.2.0,并且同时要把 gradle 版本升级到 4.6.
执行编译:
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200305165129.png)
可见这里已经执行成功了。我们再来看下刚才 app - build 下面出错的 DataBinderMapperImpl 文件:
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200305165258.png)
看下 com.example.databindinganalysis 下面的 DataBinderMapperImpl:
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200305165411.png)
可以看到,在新版本的 DataBinderMapperImpl 的 getDataBinder 方法中,google 已经把获取 ViewDataBinding 的操作进行了拆分,使用 internalGetViewDataBindingXX 对所有的 layout 文件进行分组,每个组建立了 50 个映射关系,比如 internalGetViewDataBinding0 的代码如下:
![](https://markdown-1258186581.cos.ap-shanghai.myqcloud.com/20200305165557.png)
装对 getDataBinder 的拆分很好的避免了上面遇到问题的发生。
此外,在gradle 3.1.4 版本中,app module 和 lib2 module 的 DataBinderMapperImpl 文件都是在 app module 下面的 build 文件夹下面生成的,但是在 gradle 3.2.0 版本中,会对每个 module 都生成一个 DataBinderMapperImpl 文件,这样产生 上面代码过长的问题的概率又小了许多,事实上,一个正常的项目不会再发生上面的编译错误了。
最后再贴一下 Databinding 仓库的地址:
https://android.googlesource.com/platform/frameworks/data-binding/