Reaktive-反应式Kotlin的多平台库



当今许多人喜欢反应式编程。 它具有很多优点:缺少所谓的“ 回调地狱 ”,内置的错误处理机制以及可减少错误可能性的实用编程风格。 明显更容易编写多线程代码,也更容易管理数据流(组合,拆分和转换)。

许多编程语言都有自己的反应式库:用于JVM的RxJava,用于JavaScript的RxJS,用于iOS的RxSwift,Rx.NET等。

但是我们对Kotlin有什么呢? 假设RxKotlin是合乎逻辑的。 而且确实存在这样的库,但是它只是RxJava2(所谓的“糖”)的一组扩展。

理想情况下,我希望有一个满足以下条件的解决方案:

  • 多平台-能够使用反应式编程编写多平台库并在公司内部分发;
  • 空安全性 -Kotlin类型系统保护我们免受“ 十亿美元错误 ”的影响,因此空值必须有效(例如, Observable<String?> );
  • 协方差和相反方差是Kotlin的另一个非常有用的功能,例如,可以将类型Observable<String>安全地Observable<String>Observable<CharSequence>

我们在Badoo决定不等待海边的天气,并建立了这样的图书馆。 您可能已经猜到了,我们将其称为Reaktive并将其发布在GitHub上

在本文中,我们将仔细研究Kotlin的反应式编程期望,并了解Reaktive的功能如何与之匹配。

三大自然优势


多平台


第一个自然优势是最重要的。 我们的iOS,Android和移动网络团队目前分别存在。 要求是一般的,设计是相同的,但是每个团队都做自己的工作。

Kotlin允许您编写多平台代码,但您必须忘记反应式编程。 而且我希望能够使用反应式编程来编写共享库,并在公司内部分发它们或将其上传到GitHub。 这种方法可能会大大减少开发时间并减少代码总量。

空安全


而是关于Java和RxJava2的缺陷。 简而言之,不能使用null。 让我们尝试找出原因。 看一下这个Java接口:

 public interface UserDataSource {   Single<User> load(); } 

结果可以为空吗? 为避免歧义,RxJava2中不允许null。 如果您仍然需要,那就是Maybe和Optional。 但是在科特林没有这样的问题。 我们可以说Single<User>Single<User?>不同的类型,所有问题在编译阶段都会弹出。

协方差和自变量


这是Kotlin的独特功能,而Java却很缺乏此功能。 您可以在手册中了解更多信息。 我仅给出几个有趣的示例,说明在Kotlin中使用RxJava时出现什么问题。

协方差

 fun bar(source: Observable<CharSequence>) { } fun foo(source: Observable<String>) {   bar(source) //   } 

由于Observable是Java接口,因此此类代码无法编译。 这是因为Java中的泛型类型是不变的。 您当然可以用完,但是使用诸如scan之类的运算符将再次导致编译错误:

 fun bar(source: Observable<out CharSequence>) {   source.scan { a, b -> "$a,$b" } //   } fun foo(source: Observable<String>) {   bar(source) } 

scan语句的不同之处在于其通用类型“ T”既是输入也是输出。 如果Observable是Kotlin接口,则其类型T可以表示为out,这将解决问题:

 interface Observable<out T> {   … } 

这是一个具有相反性的示例:

 fun bar(consumer: Consumer<String>) { } fun foo(consumer: Consumer<CharSequence>) {   bar(consumer) //   } 

出于与上一个示例相同的原因(Java中的通用类型是不变的),该示例无法编译。 添加将解决此问题,但同样不能解决百分之一百:

 fun bar(consumer: Consumer<in String>) {   if (consumer is Subject) {       val value: String = consumer.value //     } } fun foo(consumer: Consumer<CharSequence>) {   bar(consumer) } interface Subject<T> : Consumer<T> {   val value: T } 

好吧,按照传统,在Kotlin中,可以通过在界面中使用解决此问题:

 interface Consumer<in T> {   fun accept(value: T) } 

因此,泛型类型的可变性和矛盾性是Reaktive库的第三个自然优势。

Kotlin +反应性=反应性


我们传递给最主要的东西-Reaktive库的描述。

以下是其一些功能:

  1. 它是多平台的,这意味着您最终可以编写通用代码。 在Badoo,我们认为这是最重要的好处之一。
  2. 它是用Kotlin编写的,它为我们提供了上述优点:对null,方差/对数没有限制。 这增加了灵活性并在编译期间提供了安全性。
  3. 不依赖于其他库,例如RxJava,RxSwift等,这意味着无需将库功能带到一个共同的分母。
  4. 纯API。 例如,Reaktive中的ObservableSource接口简称为Observable ,并且所有运算符都是位于单独文件中的扩展功能。 没有15,000行的上帝课。 这样就可以轻松增加功能,而无需更改现有接口和类。
  5. 支持调度程序(使用熟悉的subscribeOnobserveOn )。
  6. 与RxJava2兼容(互操作性),提供Reaktive和RxJava2之间的源转换,并能够重用RxJava2中的调度程序。
  7. ReactiveX合规性。

由于库是用Kotlin编写的,因此我想谈一谈更多有关我们所获得的好处。

  1. 在Reaktive中,允许使用空值,因为在Kotlin中它是安全的。 以下是一些有趣的示例:
    • observableOf<String>(null) //
    • val o1: Observable<String?> = observableOf(null)
      val o2: Observable<String> = o1 // ,
    • val o1: Observable<String?> = observableOf(null)
      val o2: Observable<String> = o1.notNull() // , null
    • val o1: Observable<String> = observableOf("Hello")
      val o2: Observable<String?> = o1 //
    • val o1: Observable<String?> = observableOf(null)
      val o2: Observable<String> = observableOf("Hello")
      val o3: Observable<String?> = merge(o1, o2) //
      val o4: Observable<String> = merge(o1, o2) // ,

    变异也是一大优势。 例如,在Observable接口中,类型T被声明为out ,这使得可以编写如下内容:

     fun foo() {   val source: Observable<String> = observableOf("Hello")   bar(source) //   } fun bar(source: Observable<CharSequence>) { } 

这是今天图书馆的样子:

  • 撰写本文时的状态:alpha(可能会更改公共API);
  • 支持的平台:JVM和Android;
  • 支持的来源: ObservableMaybeSingleCompletable
  • 支持相当多的运算符,包括地图,过滤器,flatMap,concatMap,combinateLatest,zip,merge和其他(完整列表可在GitHub找到 );
  • 支持以下调度程序:计算,IO,蹦床和主程序;
  • 主题:PublishSubject和BehaviorSubject;
  • 尚不支持背压,但我们正在考虑此功能的必要性和实现。

我们对近期的计划是什么:

  • 开始在我们的产品中使用Reaktive(我们正在考虑选择);
  • JavaScript支持(请求已在审核中);
  • iOS支持
  • 在JCenter中发布工件(当前使用JitPack服务);
  • 文献资料
  • 支持的运营商数量增加;
  • 测验
  • 更多平台-欢迎提出要求!

您现在可以尝试使用该库,可以在GitHub上找到所需的一切。 分享您的经验并提出问题。 我们将感谢您的任何反馈。

Source: https://habr.com/ru/post/zh-CN447424/


All Articles