rxjava如何处理多个数据源?
How to handle multiple data sources with rxjava?
是这样的:
我有领域层为业务逻辑提供数据获取接口,我有两个数据源:本地数据库和远程网络。
它是这样工作的:
- 请求所有用户:
DataRepository.getInstance().getUsers();
- 在 DataRepository 中,有 2 个来源:
LocalDataSource.getUsers()
从本地数据库中获取所有用户,如果没有数据则忽略此请求。
RemoteDataSource.getUsers()
向我们的服务器请求最新的用户列表(即使本地数据库中有数据,为了保持数据更新),当数据被请求时,然后将其保存或更新到本地数据库并发送返回结果。
我知道我可以在 DataRepository
中实现我的目标:
public Observable<List<User>> getUsers() {
return Observable.create(new Observable.OnSubscribe<List<User>>() {
@Override
public void call(Subscriber<? super List<User>> subscriber) {
// 1. Request users from local database
List<User> localUsers = mLocalDataSource.getUsers();
if (!localUsers.isEmpty()) {
subscriber.onNext(localUsers);
}
// 2. Request the latest user list from server
// Send a retrofit2 request
Call<List<User>> call = mRemoteDataSource.getUsers();
try {
List<User> networkUsers = call.execute().body();
mLocalDataSource.saveUsers(networkUsers);
subscriber.onNext(networkUsers);
subscriber.onCompleted();
} catch (IOException e) {
subscriber.onError(e);
}
}
});
}
既然我在项目中已经使用了rxjava,为什么不使用SqlBrite和Retrofit2 RxAdapters来更方便呢?
所以 LocalDataSource.getUsers()
现在 returns Observable<List<User>>
也是 RemoteDataSource.getUsers()
.
LocalDataSource.java
public Observable<User> getUsers() {
final String sqlQuery = String.format("SELECT * FROM %s", UserTable.TABLE_NAME);
return mDatabaseHelper.createQuery(UserTable.TABLE_NAME, sqlQuery)
.mapToList(new Func1<Cursor, User>() {
@Override
public User call(Cursor c) {
return UserTable.parseCursor(c);
}
});
}
RemoteDataSource.java
public Observable<List<User>> getUsers() {
return mRetrofitApi.users();
}
问题:
在DataRepository.getUsers()
中我应该怎么做才能达到我用老把戏做的同样的事情?
public Observable<List<User>> getUsers() {
Observable<List<User>> localUsers = mLocalDataSource.getUsers();
Observable<List<User>> remoteUsers = mRemoteDataSource.getUsers()
.flatMap(new Func1<List<User>, Observable<User>>() {
@Override
public Observable<User> call(List<User> users) {
return Observable.from(users);
}
})
.doOnNext(new Action1<User>() {
@Override
public void call(User user) {
mLocalDataSource.saveUser(user);
}
})
.toList();
// What should I return to make two observables both able to emit results to the Subscriber
return Observable.concat(localUsers, remoteUsers); // ???
}
What should I do in DataRepository.getUsers() to achieve the same thing I did with the old tricks?
在这种情况下,您可以使用 concat
:
public Observable<List<User>> getUsers() {
return Observable.concat(localUsers.first(), remoteUsers);
}
但如果本地或远程结果在前并不重要,您可以使用 merge
:
public Observable<List<User>> getUsers() {
return Observable.merge(localUsers.first(), remoteUsers);
}
此外,如果您只想要一个结果(快者为胜),您可以使用amb
:
public Observable<List<User>> getUsers() {
return Observable.amb(localUsers.first(), remoteUsers);
}
是这样的:
我有领域层为业务逻辑提供数据获取接口,我有两个数据源:本地数据库和远程网络。
它是这样工作的:
- 请求所有用户:
DataRepository.getInstance().getUsers();
- 在 DataRepository 中,有 2 个来源:
LocalDataSource.getUsers()
从本地数据库中获取所有用户,如果没有数据则忽略此请求。RemoteDataSource.getUsers()
向我们的服务器请求最新的用户列表(即使本地数据库中有数据,为了保持数据更新),当数据被请求时,然后将其保存或更新到本地数据库并发送返回结果。
我知道我可以在 DataRepository
中实现我的目标:
public Observable<List<User>> getUsers() {
return Observable.create(new Observable.OnSubscribe<List<User>>() {
@Override
public void call(Subscriber<? super List<User>> subscriber) {
// 1. Request users from local database
List<User> localUsers = mLocalDataSource.getUsers();
if (!localUsers.isEmpty()) {
subscriber.onNext(localUsers);
}
// 2. Request the latest user list from server
// Send a retrofit2 request
Call<List<User>> call = mRemoteDataSource.getUsers();
try {
List<User> networkUsers = call.execute().body();
mLocalDataSource.saveUsers(networkUsers);
subscriber.onNext(networkUsers);
subscriber.onCompleted();
} catch (IOException e) {
subscriber.onError(e);
}
}
});
}
既然我在项目中已经使用了rxjava,为什么不使用SqlBrite和Retrofit2 RxAdapters来更方便呢?
所以 LocalDataSource.getUsers()
现在 returns Observable<List<User>>
也是 RemoteDataSource.getUsers()
.
LocalDataSource.java
public Observable<User> getUsers() {
final String sqlQuery = String.format("SELECT * FROM %s", UserTable.TABLE_NAME);
return mDatabaseHelper.createQuery(UserTable.TABLE_NAME, sqlQuery)
.mapToList(new Func1<Cursor, User>() {
@Override
public User call(Cursor c) {
return UserTable.parseCursor(c);
}
});
}
RemoteDataSource.java
public Observable<List<User>> getUsers() {
return mRetrofitApi.users();
}
问题:
在DataRepository.getUsers()
中我应该怎么做才能达到我用老把戏做的同样的事情?
public Observable<List<User>> getUsers() {
Observable<List<User>> localUsers = mLocalDataSource.getUsers();
Observable<List<User>> remoteUsers = mRemoteDataSource.getUsers()
.flatMap(new Func1<List<User>, Observable<User>>() {
@Override
public Observable<User> call(List<User> users) {
return Observable.from(users);
}
})
.doOnNext(new Action1<User>() {
@Override
public void call(User user) {
mLocalDataSource.saveUser(user);
}
})
.toList();
// What should I return to make two observables both able to emit results to the Subscriber
return Observable.concat(localUsers, remoteUsers); // ???
}
What should I do in DataRepository.getUsers() to achieve the same thing I did with the old tricks?
在这种情况下,您可以使用 concat
:
public Observable<List<User>> getUsers() {
return Observable.concat(localUsers.first(), remoteUsers);
}
但如果本地或远程结果在前并不重要,您可以使用 merge
:
public Observable<List<User>> getUsers() {
return Observable.merge(localUsers.first(), remoteUsers);
}
此外,如果您只想要一个结果(快者为胜),您可以使用amb
:
public Observable<List<User>> getUsers() {
return Observable.amb(localUsers.first(), remoteUsers);
}