【译】ObjectBox官方文档——响应式

转载请注明出处
第一次翻译文档,水平有限,如有任何错误和不妥,望指正。此外如想知道与RealmGreenDAO的对比请查看这篇博客
官网:ObjectBox
官方示例:Github

ObjectBox使您的应用程序可以轻松地对数据变化做出反应:

  • data observers
  • 响应式扩展
  • Rxjava扩展库

这使得在处理线程细节的同时,可以轻松地设置数据流。

响应式观察者:一个简单的例子

让我们从一个示例开始演示如何使用响应式数据观察者:

1
2
Query<Task> query = taskBox.query().equal(Task_.complete, false).build();
query.subscribe().on(AndroidScheduler.mainThread()).observer(data -> updateUi(data));//使用Lambda表达式

第一行创建一个常规的Query对象,查询task.complete == falseTask 对象。第二行将一个观察者连接到 Query 对象上。这会发生了什么:

  • 查询会在后台执行
  • 每次查询完成,观察者都会得到返回的数据
  • 在这之后,每当 Task 发生改变时,查询将再次执行
  • 一旦更新了查询结果,它们就会被发送给观察者
  • 这个观察者将在 Android 主线程调用

所以他虽然仅仅只是两行代码,但是有很多细节发生在幕后,现在,让我们深入细节。

基础数据观察者

当对象发生改变,ObjectBox 会通知订阅的观察者。这些观察者可以订阅特定类型对象的变化(通过 BoxStore)或查询结果。您需要实现io.objectbox.reactive.DataObserver接口:

1
2
3
public interface DataObserver<T> {
void onData(T data);
}

这个观察者在必要时将被 ObjectBox 调用:通常在订阅和数据更改后不久。

注意:onData()是异步调用的,并与发生数据更改的线程分离(比如提交事务的线程)。

观察常规变化

BoxStore 允许 DataObserver 订阅特定类型对象。假设您想要观察任务列表应用程序的任务对象:

1
2
DataObserver<Class> taskObserver = ...;
boxStore.subscribe(Task.class).observer(taskObserver);

现在,如果应用程序存储更多的任务,ObjectBox 将调用taskObserver.onData(task.class)来通知您更改。

注意:还有一个没有参数的subscribe()。它表示观察者订阅接收所有可用对象类的更改。

观察查询

ObjectBox 让我们构建查询来查找符合某些条件的对象。查询是 ObjectBox 的重要组成部分:每当您需要特定的数据集时,您可能会使用查询。

结合查询和观察结果,可以获得一个方便而强大的工具:当相关数据发生变化时,查询将为观察者自动提供新的结果。假设你在你的应用程序中显示了一个待办事项列表。您可以使用 DataObserver 来获取尚未完成的所有任务,并将它们传递给一个方法updateUi()(注意我们使用lambda语法):

1
2
Query<Task> query = taskBox.query().equal(Task_.completed, false).build();
query.subscribe().observer(data -> updateUi(data));

我们的观察者什么时候被调用?当一个观察者被订阅时,查询将在一个单独的线程中运行。一旦查询结果可用,它将被传递给观察者。这是对观察者的第一次调用。

现在,假设一个任务被更改并存储在 ObjectBox 中。在哪里以及如何进行都不重要;它可能是标记任务完成的用户,也可能是在与服务器同步时添加额外任务的后端线程。无论如何,查询将以更新的查询结果通知所有观察者。

请注意,此模式可以极大地简化代码:在一个地方可以让您的数据更新用户界面。没有单独的初始化代码,没有事件的连接,没有重新运行查询等。

观察者和事务

在提交事务之后发生 Observer 通知。对于某些场景,了解事务局限尤为重要。如果您调用box.put()remove(),开始并提交隐式事务。例如,这个代码片段将会在User.class上触发两次数据观察者:

1
2
box.put(friendUser);
box.put(myUser);

有几种方法可以将多个操作合并到一个事务中,例如,使用 BoxStore 类中的runInTx()callInTx()方法。对于我们的简单示例,我们可以简单地使用put()接受多个对象的重载方法:

1
box.put(friendUser, myUser);

这将产生一个事务,因此 DataObserver 只会通知一次。

订阅和取消

当你调用observer(),它将返回一个实现io.objectbox.reactive.DataSubscription接口的订阅对象:

1
2
3
4
public interface DataSubscription {
void cancel();
boolean isCanceled();
}

因此,如果您打算在以后取消订阅您的 DataObserver,那么最好保存到数据库。调用cancel()ObjectBox 知道,不应再通知观察者:

1
2
3
4
DataSubscription subscription = boxStore.subscribe().observer(myObserver);

// At some later point:
subscription.cancel();

注意:在 Android 上,通常会在onCreate()/onStart()/onResume()生命周期方法中创建订阅,并在其对应onDestroy()/onStop()/onPause()中取消它。

响应式扩展

在第一部分中,您看到了数据观察器如何帮助您保持您的应用程序最新状态。但还有更多:ObjectBox 为常规任务提供了简单而方便的响应式扩展。虽然大多数都是由 RxJava 启发的,但实际上并不是基于 RxJavaObjectBox 带来了它自己的特性,因为不是所有开发人员都熟悉 RxJava(对于RxJava ObjectBox库,参见下面)。我们不想把复杂性( Rx 几乎就像一种新语言)和 RxJava (~ 10k方法)的大小强加给每一个人。所以,我们让它保持简单和整洁。

线程调度

Android 上,UI 更新必须在主线程上进行。幸运的是,ObjectBox 允许将观察者从后台线程切换到主线程。让我们来看看上面的待办事项示例的修改版:

1
2
Query<Task> query = taskBox.query().equal(Task_.complete, false).build();
query.subscribe().on(AndroidScheduler.mainThread()).observer(data -> updateUi(data));

on()方法告诉我们希望我们的观察者被调用的位置。AndroidScheduler.mainThread()是一个内置的调度程序的实现。或者,您可以使用自定义Looper创建一个AndroidScheduler,或通过实现io.objectbox.reactive.Scheduler接口建立一个完全定制的调度器。

Data Transform

也许你想在把数据移交给观察者之前改变数据。比方说,您希望跟踪每个类型的所有存储对象的计数。BoxStore 订阅为您提供对象的类,这个示例向您展示如何将它们转换为实际的对象计数:

1
2
3
boxStore.subscribe()
.transform(clazz -> return boxStore.boxFor(clazz).count())
.observer(count -> updateCount(count));

注意,transform 操作接受一个Class类型的参数并返回一个Long类型的数值。因此 DataObserver 接收 Long 类型的对象计数作为onData()中的参数。

虽然 lambda 语法很好而且很简短,但还是让我们看一下io.objectbox.reactive.Transformer接口,以了解transform()方法作为一个参数所期望的内容:

1
2
3
public interface DataTransformer<FROM, TO> {
TO transform(FROM source) throws Exception;
}

关于 transform 的一些说明:

  • Transform 并不需要实际 “转换” 任何数据。从技术上讲,返回接收到的相同数据,并且只处理(或不使用)它是很好的。
  • Transform 总是异步执行的。可以很好的执行耗时操作。

ErrorObserver

也许您注意到一个 transformer 可能会抛出任何类型的异常。另外,DataObserver 可能抛出一个RuntimeException。在这两种情况下,您都可以创建一个 ErrorObserver 来接收发生的异常的通知。

1
2
3
public interface ErrorObserver {
void onError(Throwable th);
}

当抛出异常时,onError()将被调用,当然这需要在subscribe()之后。

Single Notifications vs. Only-Changes

当您订阅一个查询时,DataObserver 会在默认情况下得到以下两个选项:

  • 初始查询结果(在订阅之后)
  • 更新的查询结果(底层数据被更改)

有时你可能只对其中的一个感兴趣。这就是single()onlyChanges()的方法(请在subscribe()之后调用它们)。单一订阅的特殊之处在于,一旦观察者被通知,它们就会自动被取消。您仍然可以手动取消它们,以确保在某个时间点上不会对观察者进行调用。

弱引用

有时,弱引用DataObserver是件好事。注意,为了确定流,最好在可能的情况下显式地取消订阅。如果这没有吓到您,请在subscribe()后使用weak()

线程的概述

总结前面讨论过的线程:

  • 查询执行在后台线程上运行(此为任务专用)
  • DataTransformer 在后台线程上运行(此为任务专用)
  • DataObserverErrorObserver 运行在后台线程,除非调用on()方法指定一个调度器

Rxjava扩展库

通过设计,在 ObjectBox 核心没有对Rx进行任何依赖。但是正如您在 ObjectBox 中看到的那样,您仍然可以使用简单的方法来转换数据、异步处理、线程调度和一次(单个)通知。如果,您仍然想要与强大的 RxJava 2 集成(我们没有支持RxJava 1的计划)。为此,我们创建了 ObjectBox RxJava扩展库。它需要额外依赖(确保检查最新版本,可能不是这样):

1
compile 'io.objectbox:objectbox-rxjava:0.9.8-RC4'

这样,您就可以使用 RxQueryRxBoxStore 。两者都提供使用RxJava进行订阅的静态方法。

对于一般对象更改,可以使用 RxBoxStore 创建一个可观察的对象。RxQuery 允许订阅查询对象:

  • FLowable
  • Observable
  • Single

示例:

1
2
Query query = box.query().build();
RxQuery.observable(query).subscribe(this);

扩展库是在GitHub上作为一个独立的开放源码项目进行管理的。