NIUCLOUD是一款SaaS管理后台框架多应用插件+云编译。上千名开发者、服务商正在积极拥抱开发者生态。欢迎开发者们免费入驻。一起助力发展! 广告
# 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 <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingDefaultResource"> <data> </data> <LinearLayout android:id="@+id/ll" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/tv" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="test" /> </LinearLayout> </layout> ``` 然后利用脚本把这个 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 的带有 <layout> ...</layout>标签的 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/