【发布时间】:2017-08-04 15:38:04
【问题描述】:
我在 Android 上使用 RxJava 来执行登录操作。
我需要传入用户名、密码和布尔标志。用户名和密码并发送到服务器进行验证,一旦返回响应,我需要使用标志来确定下一步要做什么。
由于登录操作是异步的,我想确保当响应返回时,我仍然可以访问我一开始传入的用户名、密码和标志。
这是我编写代码的最初方式,我认为它有问题:
public Observable<Result> execute1(final String username, final String password, final boolean shouldSaveUsername) {
return mLoginNetwork
.loginWithCredentials(username, password)
.map(new Func1<Response<Void>, LoginObject>() {
@Override
public LoginObject call(Response<Void> response) {
if (!response.isSuccessful()) {
Exceptions.propagate(new HttpException(response));
}
return new LoginObject(username, password, shouldSaveUsername);
}
})
.doOnNext(new Action1<LoginObject>() {
@Override
public void call(LoginObject loginObject) {
if (loginObject.shouldSaveUsername) {
saveUsername(username);
}
}
})
.flatMap(new Func1<Entitlement, Observable<Result>>() {
@Override
public Observable<Result> call(LoginObject loginObject) {
return mNetwork
.fetchSomething();
}
});
}
当我调用 execute1() 时,它会返回一个我缓存然后订阅的 Observable。如果发生 Android 配置更改,我会取消订阅 Observable,但会将其保存在缓存中。配置更改完成后,我将 Observable 从缓存中取出并重新订阅它。当我重新订阅 loginWithCredentials 调用时,需要再次进行调用,但是当它返回用户名时,密码和布尔标志将不再存在,因此我将无法在我的链中使用它们,这是一个问题。
那么,如何解决这个问题呢?
我需要一种方法让 Observable 的输入数据成为 Observable 的一部分,这样当我缓存 Observable 时,输入数据也会被缓存。
下面是一个建议的解决方案:
public Observable<Result> execute2(String username, String password, boolean shouldSaveUsername) {
return Observable
.just(new LoginData(username, password, shouldSaveUsername))
.flatMap(new Func1<LoginData, Observable<LoginData>>() {
@Override
public Observable<?> call(final LoginData loginData) {
return mLoginNetwork
.loginWithCredentials(loginData.getUsername(), loginData.getPassword())
.map(new Func1<Response<Void>, LoginData>() {
@Override
public LoginData call(Response<Void> response) {
if (!response.isSuccessful()) {
Exceptions.propagate(new HttpException(response));
}
return loginData;
}
});
}
})
.doOnNext(new Action1<LoginData>() {
@Override
public void call(LoginData loginData) {
if (loginData.shouldSaveUsername) {
saveUsername(username);
}
}
})
.flatMap(new Func1<LoginData, Observable<Result>>() {
@Override
public Observable<Result> call(LoginData loginData) {
return mNetwork
.fetchSomething();
}
});
}
我正在尝试做的是通过使用 Observable.just() 将输入数据立即变为流的一部分,并将其转换为 Observable,然后让其余的下游操作接收它作为输入。我假设如果我现在缓存 observable 并稍后重新订阅,则输入数据现在嵌入到我的 observable 中,并且以后可以在任何运算符中访问。
我是否以“正常”的 RxJava / 功能方式解决了我提出的解决方案中的问题?有没有更好的方法来解决这个问题?
【问题讨论】:
-
是的——不使用 observables。这比使用 Thread 或 AsyncTask 并传递参数更简单吗?这段代码绝对不可读。看起来您使用 observable 只是因为它们是很酷的新事物,而不是因为它们实际上为您解决了问题。
-
@GabeSechan 这是一个简化的场景来说明我遇到的概念问题。在我的现实生活场景中,登录部分只是开始。然后我需要再进行 3 次 API 调用来获取更多数据,对其进行过滤和转换,这对于 Rxjava 来说是一个很好的用例。
-
这可能对获取的数据有意义,但对登录调用本身没有意义(我也对获取的数据表示怀疑——我发现 99% 的 RxJava 使用会降低代码的可读性和可维护的,但我会给它一个没有看到它的怀疑的好处)。但即使是这样,也没有理由将它用于无法使事情更清晰的登录。我会拒绝任何远程代码审查。