# Databinding引起的 java方法大于 65535 的问题
## 前言
项目中开发使用的是 MVVM 开发模式,并且选择了 Databinding 作为 ViewModel 和 View之间通信的桥梁,再加上一定的封装,使用起来是挺方便的,但是也会遇到一些坑,比如我遇到的这个:
**Databinding 生成的 Java 文件中方法大于 65535 问题。**
如下图所示:

## 缘由
这个问题是怎么来的呢?
其实触发条件挺小的,只有在特定的情况下才会遇到,要满足下面的条件:
1. 使用 Databinding
2. 项目要是中大型的项目(使用 Databinding 的xml文件多)
3. gradle 插件的版本要小于 3.2.0
## 模拟发生
新建项目,然后新建一个 Module:lib2,项目结构如图所示

然后分别在 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各)。
这个时候应该就会见到这个错误了:

我们看下这个报错的 DatabinderMapperImpl :

> ps:你的可能和我的不一样,xml 文件个数可能不同。
可以看到总共就那几个方法,却有2万多行代码,其中第一个 getDataBinder 方法很长,大概有15368 行,根据错误提示,可以初步断定就是就是这里的方法太大了,超过了 65535 的限制。

可以看到这个 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.
执行编译:

可见这里已经执行成功了。我们再来看下刚才 app - build 下面出错的 DataBinderMapperImpl 文件:

看下 com.example.databindinganalysis 下面的 DataBinderMapperImpl:

可以看到,在新版本的 DataBinderMapperImpl 的 getDataBinder 方法中,google 已经把获取 ViewDataBinding 的操作进行了拆分,使用 internalGetViewDataBindingXX 对所有的 layout 文件进行分组,每个组建立了 50 个映射关系,比如 internalGetViewDataBinding0 的代码如下:

装对 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/
- Java 面试题
- String、StringBuffer、StringBuilder 的区别?
- Java 中的四种引用
- 接口和抽象类的本质区别
- 集合框架
- 集合概述
- ArrayList 源码分析
- LinkedList 源码分析
- HashMap 源码分析
- LinkedHashMap 源码分析
- Android提供的 LruCache 的分析
- LinkedList 和 ArrayList 的区别
- 多线程
- 实现多线程的几种方式
- 线程的几种状态
- Thread 的 start() 和 run() 的区别
- sleep() 、yield() 和 wait() 的区别 ?
- notify() 和 notifyAll() 的区别?
- 保证线程安全的方式有哪几种?
- Synchronized 关键字
- volatile 和 synchronized 的区别?
- 如何正确的终止一个线程?
- ThreadLocal 原理分析
- 线程池
- 多线程的三个特征
- 五种线程池,四种拒绝策略,三种阻塞队列
- 给定三个线程如何顺序执行完以后在主线程拿到执行结果
- Java 内存模型
- 判定可回收对象算法
- equals 与 == 操作符
- 类加载机制
- 类加载简单例子
- 算法
- 时间、空间复杂度
- 冒泡排序
- 快速排序
- 链表反转
- IO
- 泛型
- Kolin 面试题
- Android 面试题
- Handler 线程间通信
- Message、MessageQueue、Looper、Handler 的对象关系
- Handler 使用
- Handler 源码分析
- HandlerThread
- AsyncTask
- IntentService
- 三方框架
- Rxjava
- rxjava 操作符有哪些
- 如何解决 RxJava 内存泄漏
- Rxjava 线程切换原理
- map和 flatmap 的区别
- Databinding引起的 java方法大于 65535 的问题
- Glide
- Glide 的缓存原理
- Glide 是如何和生命周期绑定的?不同的Context 有什么区别?
- Glide 、Picasso 、的区别,优劣势,如何选择?
- Jetpack
- 源码分析
- EventBus
- EventBus 源码分析
- RxBus 替代 EventBus
- OkHttp
- OkHttp 源码分析
- OkHttp 缓存分析
- RxPermission
- RxPermission 源码分析
- Retrofit
- create
- Retrofit 源码分析
- 优化
- 启动优化
- 布局优化
- 绘制优化
- 内存优化
- 屏幕适配
- 组件
- Activity
- Frgment
- Service
- ContentProvider
- BroadcastReceiver
- 进程间通信
- Binder机制和AIDL
- AILD 中的接口和普通的接口有什么区别
- in、out、inout 的区别
- Binder 为什么只需要拷贝一次
- 在android中,请简述jni的调用过程
- 生命周期
- Activity 生命周期
- Fragment 生命周期
- Service 生命周期
- onSaveInstanceState() 与 onRestoreIntanceState()
- 前沿技术
- 组件化
- 模块化
- 插件化
- 热更新
- UI - View
- Android 动画
- 事件分发机制
- WebView
- 系统相关
- 谈谈对 Context 的理解
- Android 版本
- App应用启动流程
- App 的打包
- App 的加固
- App 的安装
- Activity 启动流程
- ClassLoader
- Lru 算法加载 Bitmap 三级缓存原理
- Parcelable 和 Serializable 的区别
- Activity的启动流程
- 相关概念
- 网络相关
- Http
- Https
- Http 和 Https 的区别
- 为什么要进行三次握手和四次挥手?
- OkHttp使用Https访问服务器时信任所有证书
- 设计模式
- 单例模式
- 构建者模式
- 工厂模式
- 外观模式
- 代理模式
