android flow 背后的编程思想

654 阅读7分钟

Flow

数据流以协程为基础构建,可提供多个值。从概念上来讲,数据流是可通过异步方式进行计算处理的一组数据序列。**所发出值的类型必须相同。例如,Flow<Int> 是发出整数值的数据流。 官方网站 不了解的同学可以看下

本博客是我自己在学习一下知识点

  1. 协程
  2. flow
  3. FRP 编程范式
  4. 看了SICP 产生了对flow 框架的一些感悟。 flow 框架至今在国内不太流行,但在Android 的很多最新的库中已经广泛流行起来了比如datastorepaging3 。大多数Android 开发者也只是停留在表面使用上。

先简单概括一下。flow不是像 retrofit 以及 Gson 这样工具库。他是一种编程范式(FRP)在编程语言上体现的库。对应的库是 Rxjava (RXjava 是ReactiveX 工程 在java上的实现,他不仅仅是搭配retrofit 使用的一个库)

一些有助于思考的观点

当我们学习某项技术的时候。其实最先应该去了解他到底是为了解决什么问题的,以及他解决这个问题的核心思路是什么,最后才是学习对应的api。

我看过FRP 的编程范式的时候。有两篇博文说的比较好

  1. stackoverfkow 关于FRP的一个回答 # What is (functional) reactive programming?
  2. 什么是响应式编程(翻译)

但是最有启发的应该是《SICP》这本书的第三章 模块化、对象和状态 里面的第3.5小结流 感兴趣的可以去看这本书

然后会用我自己的思考来描述这个问题

首先我认为的了解 一些更前置的一些认识和观点

  1. 代码大全本书提到一个观点 编程是在建立一种描述问题的模型,在这样的模型,没有一个模型是完美的。我们通常说一个模型只能相对于另一个模型来解决某个问题好。 我想提这个观点是想说。面向对象(oop)就是一种模型在解决某些问题时不一定比用另一种模型会更好。 比如这里提到的流。 所以好代码 不是只要学会某些语言 + 某些框架 + 加设计模式 就能行的。
  2. sicp 开头一个应用的官邸 心智的活动。除了尽力产生各种简单的认识之外,主要表现在如下的三个方面 1 将若干简单的认识组合为一个复合的认识。由此产生各种复杂的认识,2 将两个认识放在一起做对照,不管他们呢如何简单或者复杂。在这样做的时将并不将他们合而为一,由此得到他们的相互关系的认识。3 将有关的认识于那些在实际中和他们同在的所有其他的认识隔离开,就是抽象,所具有的普遍认识都是这样得到的。 我在引用这个观点是想说,编程就是对解决某种问题的一种逐渐抽象的过程。

下面就是我自己思考的地方

先来说一下我认为OOP的代码普遍在做4类事。

  1. 将一种数据转换为另一种数据
  2. 存取数据
  3. 修改状态,读取状态
  4. 状态和数据相结合生成的新的数据或者状态

比如访问客户端访问后端接口过程就可以理解为一个数据转换为另一种数据(或者只是读取数据的过程)。 如果这样理解下来调用 一个后端接口 和本地调用某个方法返回数据从逻辑上本没有任何差别。 但是在早期(无论是okhttp 还是httpurlConnection) 来编写一个访问后端返回数据的代码。我们总是需要构建一个请求对象,然后添加回调然后得到数据。 但是在现在做一个网络请求 如果是通过retrofit 和协程来实现的话。 你会发现访问网络请求得到数据的代码和调用一个本地方法代码几乎一模一样。 我们为啥不在直接使用okhttp构建一个对象 的方式访问后端数据。因为从逻辑上来说不需要。或许我们原本需要,但是通过编写代码(抽象) 的方式将原先需要的额外操作去掉了。

但是如果一种更抽象的一种角度来看 所有的代码都是做一件事 1.一种数据转化为另一种数据 2.消费数据(存到数据库,页面UI显示)

你可能会疑惑 3 4的状态去哪了。 其实就是状态也是一种数据。甚至不仅仅是状态是一种数据,如何操作数据的函数本身也是一种数据。 这就是我想说的FRP。

理解一个新事物往往需要通过已知的是事物来参考。 oop 的核心 局部状态。

在oop的世界里核心是状态和通信(面向对象的继承封装和多态真的不是核心。只是让你更好的抽象的工具罢了) 描述对象状态的最好一句话就是

对象有状态 就是他的行为受到来自他的历史的影响。 -- 《SICP》

我们通常变量的不断的赋值来描述这种现象

问题

通过编写 反应局部状态的对象 的这个过程中逐步脱离了计算代换模式。于是在这个过程出现了很多编程的问题。因为有了状态,所以在多线程下就得考虑变量的一致性。 在测试过程中。不能通过简单输入输出来判断对象代码正确性, 因为对象的方法失去了函数的幂等性。同时我们还需要考虑对象和对象的通信。这也衍生出了一大堆的编程的问题以及解决这些问题的库(依赖注入框架,消息总线框架等等)。还有状态更新需要通知其他对象的各种生命周期的回调产生的各种bug

解决

我们能不能 不通过赋值来描述这种现象。 这就是流的模型

以数学的函数方式来思考这个问题。我们可以将一个量x随着时间变化的行为,描述为一个时间的函数x(t),如果我们想集中关注的是一个个时间的x,那么就可以将他看作一个变化着的量。然而,如果我们关注的事故这些值的整个历史,那么就不需要强调其中的变化———— 这一函数本身并没有变。 --《SICP》

我认为这就是frp的核心。我们在编程时不在关注某个时候的状态。而是关注 产生这一系列状态的函数本身。 任何表示状态的对象都可以通过一个返回flow对象的函数 来替换。 状态和数据 处理可以通过各种操作符(map reduce)来转换生成新的数据。比如显示界面和数据的持久化。 这两个地方的读取 官方推荐也是flow 比如数据的持久化 的room 或者 datastore 都是通过flow。包括compse 框架compse 的状态转化数据 也是flow

最后

本文没有讲如何使用flow 。使用的某些技术学习门槛其实很低。这个直接看官网和官网的demo 就能学会。这规则的学习总是比较简单的,但是如何使用的技巧则是困难的。就像象棋的规则人人都会,但不是每个人都是象棋大师。 理解问题和解决问题的思想 是核心。 flow api 学习我理解主要是一下的点

  1. 各种操作符的使用
  2. 背压的处理。
  3. 以及各种回调 如何转成flow 来桥接非flow的世界 (常用于的案例Editext 转flow)
  4. flow毕竟用到了协程 。所以也得学会协程

补充一些在群里看见的常见的错误 比如 认为协程和替代rxjava。 两者没有关系。 只不过目的上。协程能去消灭某些状态的能力。 这点和frp 出发点有些类似。所以有些搞混淆了。