原文链接
异步查询返回 LiveData 或 RxJava的 Maybe
, Single or Flowable.
考虑如下UI场景:用户能查看和编辑用户名。用户信息被保存在数据库中。
为了从数据库中得到用户信息,我们使用如下查询方式:
@Query(“SELECT * FROM Users WHERE id = :userId”)
User getUserById(String userId);
这种方式有两个缺点:
1. 它是阻塞的、同步调用
2. 每次用户数据被修改,需要手动调用这个方法
Room
可以观察数据库中的数据,并且通过RxJava的Maybe,Single,Flowable
来执行异步查询。
通过在observeOn
方法中设置Scheduler
,可以决定让事件发生在哪个线程。
对于返回Maybe
或者 Single
的查询,确保你在调用的subscribeOn
的Scheduler
不是AndroidSchedulers.mainThread()
。
为了使用Room 和 RxJava 2,需要在你的build.gradle
文件里添加如下依赖:
// RxJava support for Room
implementation “android.arch.persistence.room:rxjava2:1.0.0-alpha5”
// Testing support
androidTestImplementation “android.arch.core:core-testing:1.0.0-alpha5”
Maybe
@Query(“SELECT * FROM Users WHERE id = :userId”)
Maybe<User> getUserById(String userId);
- 当数据库中没有该用户时,query 返回 no rows,
Maybe
complete. - 当数据库中有一个用户时,
Maybe
会触发onSuccess
,然后complete. - 如果在
Maybe
completed 之后user有更新,nothing happens.
Single
@Query(“SELECT * FROM Users WHERE id = :userId”)
Single<User> getUserById(String userId);
- 当数据库中没有该用户时,查询返回no rows,Single将触发
onError(EmptyResultSetException.class)
. - 当数据库中有一个用户时,
Single
将触发onSuccess
. - 当
Single.onComplete
被调用后user有更新,nothing happens,因为流已经完成了。
Flowable
@Query(“SELECT * FROM Users WHERE id = :userId”)
Flowable<User> getUserById(String userId);
- 当数据库中没有该用户时,查询返回no rows,Flowable不发射数据,既没有onNext,也没有onError.
- 当数据库中有一个用户时,Flowable将触发onNext.
- 每次用户数据有更新,
Flowable
对象都会自动发出,允许你用最新的数据更新UI.
Testing
测试一个返回Maybe/Single/Flowable
的查询和测试它的同步查询没有很大的不同。 在UserDaoTest
,确保使用的是一个 in-memory 的数据库,因为当进程被杀死时,保存在这里的信息会被自动清除。
@RunWith(AndroidJUnit4.class)
public class UserDaoTest {
…
private UsersDatabase mDatabase;
@Before
public void initDb() throws Exception {
mDatabase = Room.inMemoryDatabaseBuilder(
InstrumentationRegistry.getContext(),
UsersDatabase.class)
// allowing main thread queries, just for testing
.allowMainThreadQueries()
.build();
}
@After
public void closeDb() throws Exception {
mDatabase.close();
}
添加InstantTaskExecutorRule
规则到你的测试中,以确保Room立即执行所有的数据库操作。
@Rule
public InstantTaskExecutorRule instantTaskExecutorRule =
new InstantTaskExecutorRule();
让我们执行一个测试,订阅getUserById
的发出,并检查确实在插入user时,Flowable
发出正确的数据。
@Test
public void insertAndGetUserById() {
// Given that we have a user in the data source
mDatabase.userDao().insertUser(USER);
// When subscribing to the emissions of user
mDatabase.userDao()
.getUserById(USER.getId())
.test()
// assertValue asserts that there was only one emission
.assertValue(new Predicate<User>() {
@Override
public boolean test(User user) throws Exception {
// The emitted user is the expected one
return user.getId().equals(USER.getId()) &&
user.getUserName().equals(USER.getUserName());
}
});
}
That’s it! 如果你在应用中使用RxJava 2,请使数据库处于反应状态,并确保你的UI始终显示最新数据。在这里使用Room和RxJava查看示例应用程序。