A11b95f4416661d5ac43b2daa94a6d0e
重学安卓:是 事关软件工程安全 的 数据驱动 UI 框架 扫盲干货

温馨提示:如果这是第一次接触《重学安卓》,可借助 这份在 GitBook 上维护的 “导读” 来快速了解《重学安卓》专栏、获取它的目录、试读内容,以及了解它的最新动态 和 发展状况。

截至目前,专栏已对 体系化文章 做了 730 余次修订,数十位群友告诉我 受专栏的启发 他们也开启了写作之路。群里不定期会有小伙伴讨论适配问题、分享原创的开源库 和 提供内推机会,订阅后可随时进群交流。

·

原来是这么回事啊 —— 从没有人给我这样讲过 🙉

前言

很高兴见到你!

近来时有读者私下询问,如何理解 Flutter 与 Android 的区别,

对此我明白他们实际想了解的是:有没有切入点,能够正确理解 Flutter UI 的本质,以便能够笃信地快速上手,

答案是肯定的。😉

跨平台开发已成现实

金三银四,相信有不少读者都在忙着准备面试。

Android 市场已今非昔比。在过去,迫于招人的压力,应试者只需了解四大组件、视图、网络请求,即可谋得一份满意的工作。

现如今,随着跨平台技术的逐渐成熟和大规模应用,许多企业在招聘要求上都会标注 有 React Native / Flutter 开发经验者优先,或者,就算面试时不强求,入职后也多多少少要求快速上手,以应对层出不穷的页面需求。

然而,不知你有没有发现,尽管 Flutter 1.X 已面世 2 年、Fuchsia 已宝剑出鞘,社区里飙车的,来来去去也就那么几个,

是网上关于 Flutter 的文章太少了吗?不是的,恰恰相反,Flutter UI 的网文多如牛毛,却没有一篇 愿意费哪怕一丝丝笔墨,来从 根源的根源 为人们介绍 为什么要用 Flutter UI、Flutter UI 的本质到底为何,

这使得 多数人只能 年复一年地、眼睁睁地看着别人上车,却不知从何下手、如何抓住本质 —— 以不变应万变地玩转 这个新时代的 UI 框架。

本文的目标

好消息是,本人拥有实打实 3 年的 移动端架构 践行和设计经验,对 MVVM 架构在确立规范化、标准化 开发模式 以减少不可预期的错误 所作的努力,有着深入的理解。

Flutter UI 的本质是数据驱动,数据驱动是 MVVM 两大特点之一,并且数据驱动最为普遍的实现方式是 函数式编程,

因而本文的目标,就是结合前几期我们介绍过的 《RxJava 钥匙》 以及 《Jetpack DataBinding》,来举一反三地演绎一下:

作为现代化的编程范式,函数式编程 究竟为 快速开发过程中 减少不可预期的错误 做了哪些努力。

不同于 东拼西凑、人云亦云、徒添困扰 的网文,愿意将 函数式编程 的 深度思考知识实战反思经验 无保留地分享,全网仅此一家。这样的文章可以说是 看一篇、少一篇,因此,就算不去 hold 住面试官,也请务必跟随本文的脚步,无障碍地将 Flutter UI 本质的本质 过一遍!😉

文章目录一览

  • 前言
  • 跨平台开发已成现实
  • 数据驱动的本质是函数式编程
  • 纯函数是函数式编程的基石
  • 函数式编程引入前的混沌世界
  • 函数式编程为什么能(彻底)解决这个问题?
  • 引入函数式编程后的世界
  • 所以数据驱动 UI 框架是怎么设计的呢?
  • 数据驱动有什么不可替代的好处呢?
  • 函数式编程的局限
  • Note 2020.04.27 加餐:
  • 现有条件下解决 视图调用一致性问题 的最优解
    • DataBinding 严格模式
    • kotlin DSL 动态布局
  • Note 2020.07.31 加餐:
    • 通过 函数式编程思想 秒懂 数据驱动 UI 框架 的打开方式
  • 综上

数据驱动的本质是函数式编程

数据驱动、Java8 Stream、RxJava 等,本质上都是函数式编程。

许多文章,从 RxJava 时代起,就照搬了官网的说法,说 RxJava 是一种 “响应式编程” 框架。

事实上,“响应式编程” 是一种 额外发明的称谓,“响应式编程” 这个概念 不仅无助于我们 正确理解事实,反倒徒添困扰 —— 为什么要用响应式编程 —— 没有人真的能够 就这个脱离事实的概念 把事情给你交代清楚

所以,我们不妨回归它最真实的本质 —— 函数式编程,好从根源的根源找寻线索,从而有机会 知其所以然、而顿悟般地 知其然。

纯函数是函数式编程的基石

老规矩,先讲结论:

Flutter UI 的本质是数据驱动框架,数据驱动的本质是函数式编程,而纯函数是函数式编程的基石

函数式编程的存在,主要是为了 从范式层面彻底解决 过程的一致性问题

数据驱动 主要是为了解决 视图调用的一致性问题

如果光是阅读了以上三点,你还是不理解的话,那接下来我就分别介绍 99% 的网文都不曾介绍的真实状况,来方便你迅速地建立起感性的认识。😉

函数式编程引入前的混沌世界

以下是我们最常见的用法:

通过 findViewById 拿到 TextView 实例,使其作为 Activity 内部的共享成员变量,为多个方法所调用,来改变 TextView 的状态。

这造成了什么问题呢?

一旦 TextView 成为共享变量,被分散到各个方法中,后续就不可控了,因为当执行方法 B 时,方法 A 是无法知道 方法 B 中对共享变量做了什么,却无差别地承受 共享变量被修改 所带来的影响,

比如当 方法 B 将 TextView 置空,那么 方法 A 调用 TextView 实例时 将面临 null 安全问题。

可能有人会问,这种问题 通过手动判空 不就可以了?

事实上,在软件工程的背景下,任何微小的隐患都可能被指数级地放大。

一个软件的页面可能有数十个,每个页面的控件也可能多达十数个,而每个控件都可能分散在多个方法中,这种情况下,一味地寄希望于手动判空,是成本极高 且存在 一致性风险 的 —— 总会有疏忽的时候,总会有 方法 A 记得判空,而方法 B 忘记的时候。

函数式编程为什么能(彻底)解决这个问题?

因为函数式编程基于纯函数。

什么是纯函数?为什么纯函数最终能解决这个问题?

简单来说,纯函数相比普通函数的特征是:

只有一个入口 & 只有一个出口

啥意思呢,就是说:

函数只从 参数列表 这一个入口 接收外来的初值,

并且只从 返回值 这个一个出口 返回结果数据。

除此之外:

不在函数内部执行与运算本身无关的其他操作,

不在函数内部调用外部变量、不修改从外部传进来的变量,

(简言之就是,调用函数时 没有"副作用",且 不改变初始状态

抛开上述 TextView 的案例,我们先来举个例子看看纯函数本身:

如此一来,在调用该函数时,关注点就只有 入口和出口 这两处,而不至于蔓延到整个程序,从而 不可预期情况发生的概率 从 99.9% 骤减为 0 —— 调用者无须了解细节 即可放心调用。

引入函数式编程后的世界

函数式编程,除了单个纯函数,也可以是多个纯函数的链式编程,

即,上一个函数的输出作为下一个函数的输入

整个链同样只有 开头的入参 这一个入口,和 末尾的回调 这一个出口,

top Created with Sketch.