每日一问:不一样的角度吐槽下 DataBinding
我们项目采用的是 kotlin && DataBinding 处理的,可能你会疑问,既然用的是 kotlin,为啥没有用 kotlinx?新的页面当然是用的 kotlinx 啦,但我们有相当庞大的历史代码,并且我们的通用 adapter 其实也是基于 DataBinding 来封装的。所以,我们还是不得不来讨(吐)论(槽)一下这个 DataBinding 的坑。事实上,这个问题在我当年面试字节跳动的时候就被问及过。
这是一个非常开放性的问题,所以看到这篇文章的小伙伴一定得带一个有色眼镜进行审视,下面会尽可能地列举中笔者遇到过的坑,当然能力有限,有可能不少东西并不是 DataBinding 的问题,需要大家一起来甄别并进行补充。
DataBinding 是早些时候 Google 推出来的一个支持库,只要勇于解决我们代码中频繁出现的 findViewById()
问题,在此之前,我相信大部分人都听过或者使用过 Butterknife,截止目前,该库都已经更新到 10.1.0 了,而且作者是 JakeWharton 大神,实为相当好用。
但我们今天不对 Butterknife 进行过多的评论,而转移到我们的主角 DataBinding。
对于 DataBinding 的使用和介绍这里就不做讲述了,建议大家还是直接到 官方文档
进行查阅学习,今天在这儿,主要和大家分享讨论一下在笔者 1 年多的 DataBinding 中踩过的一些坑。我想这些 tips 肯定不是全部,并且有些其实是个人误操所致,不管怎么说,我们今天就舍弃对它的赞美,吐槽吐槽这个 DataBinding。
极难进行错误定位
oh,这应该是使用 DataBinding 的小伙伴们最最最大的坑点了,你甚至一定在各种技术轮胎和开发交流群中见过一些小伙伴对它的深恶痛绝。不管你代码中有了什么错误,你一定收到的错误日志是一大堆 DataBinding 生成的类找不到。
可能非常多的小伙伴会对此保持疑问,这不,真正的错误都在 build 日志的最后能看到么?实在没有看到,你也可以在控制台输入 ./gradlew build --stacktrace
查看到详细日志。
但事实真的如此么?有没有小伙伴经历过不管你怎么查看日志都找不到真正的错误代码的。
我想一定有!虽然我们一般都要求对 commit 保持「少量多次」的原则,但这并不能保证我们时刻都得严格遵守,在部分业务非常紊乱的时候,我们并不希望经常做 commit,因为每一次 commit,我们也得做一次编译确认当前的代码没有任何语法问题。而对于代码量庞大、尤其是还受各种历史原因组件化不够彻底的项目,一次编译基本就可以让你去吃一个饭了。我司的项目就是如此,受限于历史原因,组件化做的并不彻底,导致本地编译在没有命中缓存的情况下至少得 10 分钟,这还是在 16G 的 mac pro 上。而且大多数人的电脑可能配置还更低,在编译的时候很难做其他和工作相关的事情。
为了处理这个编译问题,我司不得不编写一个编译脚本,把编译操作都在服务器上进行处理,在做了一系列优化操作后,在没命中缓存的情况下编译 debug 包还是需要 4.5 分钟,当然现在在编译中我们可以做其他有意思的事情了。
好像前面这两段说了很多无关紧要的东西,但是!我真正想要表达的是,这时候出现了错误,并且日志无法对错误进行定位,你会发现非常痛苦,你可能已经改动了数十个文件,新建了不少 XML,因为无法定位到日志,你不得不一行一行的进行语法检查。
我在这个问题上体验过编码两小时,查编译失败问题花费时间更多的尴尬情况,通常来说,这样的错误都不容易发现,我深深的记住我有一次因为组件化历史问题,不得不把一个组件代码 move 到另外一个组件,然后就发生了不可预料的错误的悲痛场景。当你 stash
改动后就可以编译,但 pop
出来就错误的时候,你才会知道什么是手段极其残忍。
OK,目前对于这样的情况,还是有所总结,这种情况 95% 是 XML 代码的问题,你可以直接检查 XML 了。
代码极难维护
我们使用 DataBinding 的时候,必然会喜欢它的双向绑定操作。在 XML 里面直接做一些简单的逻辑处理,这样的操作让我们的代码变得非常简洁,并且可以省去 findViewById()
带来的不必要的性能损耗。但这样的操作让后续维护功能代码的人非常痛苦。
我们的代码里面就有相当部分的这样的代码,部分页面逻辑非常复杂,比如一个商品详情页,会牵扯到非常大量的信息展示和各种促销秒杀状态,还有不少的动效,这时候不少逻辑放在 XML 里面后,后续维护的同事在处理产品改动的时候,一定在心中暗自谩骂。
通常来说,我习惯于把这些复杂的逻辑放置在我们的 Java 代码中。就目前来看,在代码中查看这些复杂的交互逻辑得心应手不少。
@{} 不会去做检查
早些时候,我连续犯过两次低级错误,所以对这个问题记忆深刻。我们可以发现,XML 里面支持我们用类似 @{}
这样的方式去为 TextView
设置显示内容,但在 XML 里面我们并没有检测机制,所以极易出现原本你这个是一个 Number
类型的值,编译器却当做一个 resourceId
进行处理而报错。实际上,在代码里面设置编译器是会直接报错的。
根据控件 ID 在代码里面找一个东西很难
在我们项目的早期代码中,XML 里面的命名规范基本是 xxx_xxx_xxx 这样的格式,但在 DataBinding 里面为我们生成的变量却采用的是驼峰命名法,这导致我们根据一个控件 id 去对应 class 里面寻找的时候,还得自己更改为驼峰命名法命名的名字,这一度让我们感到非常不适,所以我们后面的代码 XML 命名规范就跟着变成驼峰命名法了。这可能和命名规范有些许出入,不过我们坚信适合自己的,才是最好的理念。
部分 XML 中的表达式在不同 gradle 版本上表现有所不同
前面说到,我们平时会采用服务器编译,所以此前有出现过 XML 文件里面的某个属性设置在本地编译不过,但在服务器上甚至其他同事的电脑上可以编译的问题。老实说,目前我并没有找到真正的原因,我姑且把这个问题甩锅给了此前做这个的同事。我后续更改了他的实现方式才让这个问题得到妥善处理,但至今没有明白问题出在哪里,因为那样的方式我认为本身代码是没有什么问题的。可惜我现在没有时间去寻找这个代码,大概是设置一个公用的 onClickListener
的问题。
BindingAdapter 不好维护
我们通常会用到 @BindingAdapter 方式来做一些公用逻辑,而不是直接去把逻辑放在页面通过设置属性来使用它,这样就会出现这些公用逻辑比较难维护,当然,这极有可能是我们项目的历史问题,但我觉得这算是一个坑点了。不知道有没有人出现这样的属性在 XML 里面没有提示的情况。就像你自定义 View Styleable 名字不唯一一样。
多模块依赖问题
这个问题我之前还没有发现,因为我们每个模块都用到了 DataBinding,所以认为每个模块的 gradle
都设置上 DataBinding 的配置,并不算什么令人可以吐槽的事,但看起来这个问题挺严重的,所以也在这分享给大家。转自 wanAndroid 上面 xujiafeng 的回答}
DataBinding在多模块开发的时候,有这样一个机制:
如果子模块使用了 DataBinding,那么主模块也必须在 gradle 加上配置,不然就会报错;
如果主模块和子模块都添加上了 DataBinding 的配置,那么在编译时,子模块的 XML 文件产生的 Binding 类除了在自己的 build 里会有一份外,在主模块下也会有一份。
那么,如果主模块与子模块都有一个 layout 根目录的 activity_main.xml,主模块生成的 ActivityMainBinding 会是根据子模块的文件生成的!这种情况我们还可以通过让主模块和子模块使用不同的命名,那么下面这个问题就更要命了:如果子模块的某个 XML 文件使用了一些第三方的控件,那么主模块由于也会生成这个文件的 Binding 类,并且其会有第三方控件的引用,这时候由于主模块没有引入这些控件,就会报错,解决办法是在子模块应用第三方控件的时候,使用 API 的方式应用,这样主模块就坚决引用到了这些第三方控件,这是这样违背了解耦的原则。
哎,个人能力有限,就想到哪儿说到哪儿了。可能有不少其实并不是 databinding 的坑,是个人使用问题,还往明白的人能直接指出。PS:再给我一个机会,我不想在用 Databinding。
希望有其他槽点或者认为上面的东西是有更好的处理方式的小伙伴一定要在下面留言,盼复盼复~
每日一问:不一样的角度吐槽下 DataBinding的更多相关文章
- 每日一问:Android 消息机制,我有必要再讲一次!
坚持原创日更,短平快的 Android 进阶系列,敬请直接在微信公众号搜索:nanchen,直接关注并设为星标,精彩不容错过. 我 17 年的 面试系列,曾写过一篇名为:Android 面试(五):探 ...
- 每日一问:谈谈 volatile 关键字
这是 wanAndroid 每日一问中的一道题,下面我们来尝试解答一下. 讲讲并发专题 volatile,synchronize,CAS,happens before, lost wake up 为了 ...
- 每日一问:讲讲 Java 虚拟机的垃圾回收
昨天我们用比较精简的文字讲了 Java 虚拟机结构,没看过的可以直接从这里查看: 每日一问:你了解 Java 虚拟机结构么? 今天我们必须来看看 Java 虚拟机的垃圾回收算法是怎样的.不过在开始之前 ...
- 每日一问:你了解 Java 虚拟机结构么?
对于从事 C/C++ 程序员开发的小伙伴来说,在内存管理领域非常头疼,因为他们总是需要对每一个 new 操作去写配对的 delete/free 代码.而对于我们 Android 乃至 Java 程序员 ...
- 每日一问:LayoutParams 你知道多少?
前面的文章中着重讲解了 View 的测量流程.其中我提到了一句非常重要的话:View 的测量匡高是由父控件的 MeasureSpec 和 View 自身的 `LayoutParams 共同决定的.我们 ...
- 每日一问:简述 View 的绘制流程
Android 开发中经常需要用一些自定义 View 去满足产品和设计的脑洞,所以 View 的绘制流程至关重要.网上目前有非常多这方面的资料,但最好的方式还是直接跟着源码进行解读,每日一问系列一直追 ...
- Dapper小型ORM的使用(随便吐槽下公司)
近来公司又有新项目要做,之前做项目用过蛮多ORM,包括ef,NetTiers,ServiceStack.OrmLite等ROM,每种ORM都有一定的坑(或者说是使用者的问题吧~~).用来用去都觉的有一 ...
- 吐槽下近期的4G手机:
吐槽下近期的4G手机: 1.iphone6和6p,分别是4.7和5.5吋屏,1810和2915毫安时不可拆卸电池,双核64位苹果A8处理器.电池容量太小,不经用,中度使用一天一充,而且不支持VOOC闪 ...
- android 深入浅出 群内“每日一问” 问答总结
永远不变的就是变. 俗话说的好,环境改变人生. 常常面对的是一群积极奋进的人,那么你的心态和生活也会变的充满斗志.青春在于折腾,趁我们还年轻,拿出你的激情.踏着泪水载着梦,才干拥有自己的一片天空. 上 ...
随机推荐
- scala基础题--函数可以没有返回值案例,编写一个函数,从终端输入一个整数,打印出对应的金字塔
函数可以没有返回值案例,编写一个函数,从终端输入一个整数,打印出对应的金字塔 import scala.io.StdIn object work02 { def main(args: Array[St ...
- Redis(九)高可用专栏之《简介篇》
在互联网的大趋势下,用户体验.服务的可用性日趋重要.任何一个服务的不可用,都可能导致连锁式功能故障. 前言 高可用模型的已经逐渐形成一种套路: 主备/主从模式 集群模式 主备/主从模式 至少有两台服务 ...
- 深入V8引擎-AST(5)
懒得发首页了,有时候因为贴的代码太多会被下,而且这东西本来也只是对自己学习的记录,阅读体验极差,所以就本地自娱自乐的写着吧! 由于是解析字符串,所以在开始之前介绍一下词法结构体中关于管理字符串类的属性 ...
- PyTorch 之 DataLoader
DataLoader DataLoader 是 PyTorch 中读取数据的一个重要接口,该接口定义在 dataloader.py 文件中,该接口的目的: 将自定义的 Dataset 根据 batch ...
- PIE SDK矢量栅格化算法
1.算法功能简介 矢量栅格化,由矢量数据向栅格数据的转换一般比较方便.对于点.线目标,由其所在的栅格行.列数表示,对于面状目标,则需判定落人该面积内的像元.通常栅格(像元)尺寸均大于原来坐标表示的分辨 ...
- python--进程初识详解
进程:通俗理解一个运行的程序或者软件,进程是操作系统资源分配的基本单位 1.1.导入进程模块 import multiprocessing 1.2.Process进程类的语法结构如下: Process ...
- Composer安装laravel框架
一.打开CMD,进入想安装的目录,输入如下图所示,安装一个blog的项目: 二.进入指定目录即可看到生成的blog项目,如下图:
- 5G:为人工智能与智能制造赋能
近几年,全球有两大科技领域越来越热:一个是人工智能,另一个是5G.两者都是能够改变时代.改变社会.改变经济的颠覆性技术.目前,我国已经发放了四张5G牌照,5G产业处在爆发前夜的阶段:人工智能方面,业界 ...
- 推荐收藏 —— MySQL视图详细介绍
前言: 在MySQL中,视图可能是我们最常用的数据库对象之一了.那么你知道视图和表的区别吗?你知道创建及使用视图要注意哪些点吗?可能很多人对视图只是一知半解,想详细了解视图的同学看过来哟,本篇文章会 ...
- Centos7 安装腾达U12驱动无线网卡
解决过程: 办法一: CentOS7.3 默认的内核版本较低,为 3.10.0-514.el7.x86_64. 无论是使用腾达官方提供的驱动,还是github 上的驱动(链接 https://gith ...