【译】ObjectBox官方文档——响应式
【译】ObjectBox官方文档——响应式
转载请注明出处
第一次翻译文档,水平有限,如有任何错误和不妥,望指正。此外如想知道与Realm 和 GreenDAO的对比请查看这篇博客
官网:ObjectBox
官方示例:Github
ObjectBox使您的应用程序可以轻松地对数据变化做出反应:
- data observers
- 响应式扩展
- Rxjava扩展库
这使得在处理线程细节的同时,可以轻松地设置数据流。
响应式观察者:一个简单的例子
让我们从一个示例开始演示如何使用响应式数据观察者:
1 | Query<Task> query = taskBox.query().equal(Task_.complete, false).build(); |
第一行创建一个常规的Query对象,查询task.complete == false
的 Task 对象。第二行将一个观察者连接到 Query 对象上。这会发生了什么:
- 查询会在后台执行
- 每次查询完成,观察者都会得到返回的数据
- 在这之后,每当 Task 发生改变时,查询将再次执行
- 一旦更新了查询结果,它们就会被发送给观察者
- 这个观察者将在 Android 主线程调用
所以他虽然仅仅只是两行代码,但是有很多细节发生在幕后,现在,让我们深入细节。
基础数据观察者
当对象发生改变,ObjectBox 会通知订阅的观察者。这些观察者可以订阅特定类型对象的变化(通过 BoxStore)或查询结果。您需要实现io.objectbox.reactive.DataObserver
接口:
1 | public interface DataObserver<T> { |
这个观察者在必要时将被 ObjectBox 调用:通常在订阅和数据更改后不久。
注意:onData()
是异步调用的,并与发生数据更改的线程分离(比如提交事务的线程)。
观察常规变化
BoxStore 允许 DataObserver 订阅特定类型对象。假设您想要观察任务列表应用程序的任务对象:
1 | DataObserver<Class> taskObserver = ...; |
现在,如果应用程序存储更多的任务,ObjectBox 将调用taskObserver.onData(task.class)
来通知您更改。
注意:还有一个没有参数的subscribe()
。它表示观察者订阅接收所有可用对象类的更改。
观察查询
ObjectBox 让我们构建查询来查找符合某些条件的对象。查询是 ObjectBox 的重要组成部分:每当您需要特定的数据集时,您可能会使用查询。
结合查询和观察结果,可以获得一个方便而强大的工具:当相关数据发生变化时,查询将为观察者自动提供新的结果。假设你在你的应用程序中显示了一个待办事项列表。您可以使用 DataObserver 来获取尚未完成的所有任务,并将它们传递给一个方法updateUi()
(注意我们使用lambda语法):
1 | Query<Task> query = taskBox.query().equal(Task_.completed, false).build(); |
我们的观察者什么时候被调用?当一个观察者被订阅时,查询将在一个单独的线程中运行。一旦查询结果可用,它将被传递给观察者。这是对观察者的第一次调用。
现在,假设一个任务被更改并存储在 ObjectBox 中。在哪里以及如何进行都不重要;它可能是标记任务完成的用户,也可能是在与服务器同步时添加额外任务的后端线程。无论如何,查询将以更新的查询结果通知所有观察者。
请注意,此模式可以极大地简化代码:在一个地方可以让您的数据更新用户界面。没有单独的初始化代码,没有事件的连接,没有重新运行查询等。
观察者和事务
在提交事务之后发生 Observer 通知。对于某些场景,了解事务局限尤为重要。如果您调用box.put()
或remove()
,开始并提交隐式事务。例如,这个代码片段将会在User.class
上触发两次数据观察者:
1 | box.put(friendUser); |
有几种方法可以将多个操作合并到一个事务中,例如,使用 BoxStore 类中的runInTx()
或callInTx()
方法。对于我们的简单示例,我们可以简单地使用put()
接受多个对象的重载方法:
1 | box.put(friendUser, myUser); |
这将产生一个事务,因此 DataObserver 只会通知一次。
订阅和取消
当你调用observer()
,它将返回一个实现io.objectbox.reactive.DataSubscription
接口的订阅对象:
1 | public interface DataSubscription { |
因此,如果您打算在以后取消订阅您的 DataObserver,那么最好保存到数据库。调用cancel()
让 ObjectBox 知道,不应再通知观察者:
1 | DataSubscription subscription = boxStore.subscribe().observer(myObserver); |
注意:在 Android 上,通常会在onCreate()
/onStart()
/onResume()
生命周期方法中创建订阅,并在其对应onDestroy()
/onStop()
/onPause()
中取消它。
响应式扩展
在第一部分中,您看到了数据观察器如何帮助您保持您的应用程序最新状态。但还有更多:ObjectBox 为常规任务提供了简单而方便的响应式扩展。虽然大多数都是由 RxJava 启发的,但实际上并不是基于 RxJava 。ObjectBox 带来了它自己的特性,因为不是所有开发人员都熟悉 RxJava(对于RxJava ObjectBox库,参见下面)。我们不想把复杂性( Rx 几乎就像一种新语言)和 RxJava (~ 10k方法)的大小强加给每一个人。所以,我们让它保持简单和整洁。
线程调度
在 Android 上,UI 更新必须在主线程上进行。幸运的是,ObjectBox 允许将观察者从后台线程切换到主线程。让我们来看看上面的待办事项示例的修改版:
1 | Query<Task> query = taskBox.query().equal(Task_.complete, false).build(); |
on()
方法告诉我们希望我们的观察者被调用的位置。AndroidScheduler.mainThread()
是一个内置的调度程序的实现。或者,您可以使用自定义Looper
创建一个AndroidScheduler
,或通过实现io.objectbox.reactive.Scheduler
接口建立一个完全定制的调度器。
Data Transform
也许你想在把数据移交给观察者之前改变数据。比方说,您希望跟踪每个类型的所有存储对象的计数。BoxStore 订阅为您提供对象的类,这个示例向您展示如何将它们转换为实际的对象计数:
1 | boxStore.subscribe() |
注意,transform 操作接受一个Class
类型的参数并返回一个Long
类型的数值。因此 DataObserver 接收 Long
类型的对象计数作为onData()
中的参数。
虽然 lambda 语法很好而且很简短,但还是让我们看一下io.objectbox.reactive.Transformer
接口,以了解transform()
方法作为一个参数所期望的内容:
1 | public interface DataTransformer<FROM, TO> { |
关于 transform 的一些说明:
- Transform 并不需要实际 “转换” 任何数据。从技术上讲,返回接收到的相同数据,并且只处理(或不使用)它是很好的。
- Transform 总是异步执行的。可以很好的执行耗时操作。
ErrorObserver
也许您注意到一个 transformer 可能会抛出任何类型的异常。另外,DataObserver 可能抛出一个RuntimeException
。在这两种情况下,您都可以创建一个 ErrorObserver 来接收发生的异常的通知。
1 | public interface ErrorObserver { |
当抛出异常时,onError()
将被调用,当然这需要在subscribe()
之后。
Single Notifications vs. Only-Changes
当您订阅一个查询时,DataObserver 会在默认情况下得到以下两个选项:
- 初始查询结果(在订阅之后)
- 更新的查询结果(底层数据被更改)
有时你可能只对其中的一个感兴趣。这就是single()
和onlyChanges()
的方法(请在subscribe()
之后调用它们)。单一订阅的特殊之处在于,一旦观察者被通知,它们就会自动被取消。您仍然可以手动取消它们,以确保在某个时间点上不会对观察者进行调用。
弱引用
有时,弱引用DataObserver
是件好事。注意,为了确定流,最好在可能的情况下显式地取消订阅。如果这没有吓到您,请在subscribe()
后使用weak()
。
线程的概述
总结前面讨论过的线程:
- 查询执行在后台线程上运行(此为任务专用)
- DataTransformer 在后台线程上运行(此为任务专用)
- DataObserver 和 ErrorObserver 运行在后台线程,除非调用
on()
方法指定一个调度器
Rxjava扩展库
通过设计,在 ObjectBox 核心没有对Rx进行任何依赖。但是正如您在 ObjectBox 中看到的那样,您仍然可以使用简单的方法来转换数据、异步处理、线程调度和一次(单个)通知。如果,您仍然想要与强大的 RxJava 2 集成(我们没有支持RxJava 1的计划)。为此,我们创建了 ObjectBox RxJava扩展库。它需要额外依赖(确保检查最新版本,可能不是这样):
1 | compile 'io.objectbox:objectbox-rxjava:0.9.8-RC4' |
这样,您就可以使用 RxQuery 和 RxBoxStore 。两者都提供使用RxJava进行订阅的静态方法。
对于一般对象更改,可以使用 RxBoxStore 创建一个可观察的对象。RxQuery 允许订阅查询对象:
- FLowable
- Observable
- Single
示例:
1 | Query query = box.query().build(); |
扩展库是在GitHub上作为一个独立的开放源码项目进行管理的。