我正在寻找等待异步任务在rx-java中完成的最佳方法。
作为一个常见的例子,有一个函数可以从本地存储中获取一个id列表,然后在远程系统中查询这些ID,然后将远程系统结果合并为一个报告并返回给函数的调用者。由于对远程系统的调用很慢,我们希望它们以异步方式完成,我只想在所有调用都返回并且其结果已经处理后返回。
我发现这样做的唯一可靠方法是轮询订阅以检查它是否已取消订阅。但我的想法似乎并不像是RX' RX'做事的方式!
作为一个例子,我从http://howrobotswork.wordpress.com/2013/10/28/using-rxjava-in-android/中取了一个例子并稍微修改它以使其成为非机器人并显示我的意思。我必须在main()方法中使用以下代码来阻止它立即退出。
while (!subscription.isUnsubscribed()) {
Thread.sleep(100);
}
下面列出了该示例的完整代码(如果您尝试编译它,则依赖于http://square.github.io/retrofit/)
package org.examples;
import retrofit.RestAdapter;
import retrofit.http.GET;
import retrofit.http.Query;
import rx.Observable;
import rx.Subscriber;
import rx.Subscription;
import rx.functions.Action0;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
public class AsyncExample {
public static void main(String[] args) throws InterruptedException {
final Subscription subscription = Observable.from("London", "Paris", "Berlin")
.flatMap(new Func1<String, Observable<WeatherData>>() {
@Override
public Observable<WeatherData> call(String s) {
return ApiManager.getWeatherData(s);
}
})
.subscribe(
new Action1<WeatherData>() {
@Override
public void call(WeatherData weatherData) {
System.out.println(Thread.currentThread().getName() + " - " + weatherData.name + ", " + weatherData.base);
}
},
new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
System.out.println(Thread.currentThread().getName() + " - ERROR: " + throwable.getMessage());
}
},
new Action0() {
@Override
public void call() {
System.out.println(Thread.currentThread().getName() + " - COMPLETED");
}
}
);
// Have to poll subscription to check if its finished - is this the only way to do it?
while (!subscription.isUnsubscribed()) {
Thread.sleep(100);
}
}
}
class ApiManager {
private interface ApiManagerService {
@GET("/weather")
WeatherData getWeather(@Query("q") String place, @Query("units") String units);
}
private static final RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("http://api.openweathermap.org/data/2.5")
.build();
private static final ApiManagerService apiManager = restAdapter.create(ApiManagerService.class);
public static Observable<WeatherData> getWeatherData(final String city) {
return Observable.create(new Observable.OnSubscribe<WeatherData>() {
@Override
public void call(Subscriber<? super WeatherData> subscriber) {
try {
System.out.println(Thread.currentThread() + " - Getting " + city);
subscriber.onNext(apiManager.getWeather(city, "metric"));
subscriber.onCompleted();
} catch (Exception e) {
subscriber.onError(e);
}
}
}).subscribeOn(Schedulers.io());
}
}
答案 0 :(得分:5)
如果您正在询问如何将异步Observable转换为同步Observable,您可以使用.toBlocking()然后使用.last()等待Observable完成。例如,在计时器完成之前,以下内容将不会继续虽然计时器在计算线程上执行。
try {
Observable
.timer(10, TimeUnit.SECONDS)
.toBlocking()
.last(); // Wait for observable to complete. Last item discarded.
} catch (IllegalArgumentException ex) {
// No items or error.
}
除非绝对必要,否则您可能不应该使用此方法。通常,应用程序终止将由其他一些事件(按键,关闭菜单项单击等)控制。您的网络请求Observables将异步完成,您将在onNext(),onCompleted()和onError()调用中执行操作更新UI或显示错误。
此外,Retrofit的优点在于它内置了对RxJava的支持,并将在后台执行网络请求。要使用该支持,请将接口声明为返回Observable。
interface ApiManagerService {
@GET("/weather")
Observable<WeatherData> getWeather(@Query("q") String place, @Query("units") String units);
}
这允许您将getWeatherData()方法简化为:
public static Observable<WeatherData> getWeatherData(final String city) {
return apiManager.getWeather(city, "metric");
}
答案 1 :(得分:3)
为了同步等待异步观察,我写了一个简单的函数:
public static <T> void runSynchronously(Observable<T> observable) {
final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference<Throwable> exception = new AtomicReference<>();
observable.subscribe(new Subscriber<T>() {
@Override
public void onCompleted() {
latch.countDown();
}
@Override
public void onError(Throwable e) {
exception.set(e);
latch.countDown();
}
@Override
public void onNext(T o) {
}
});
try {
latch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (exception.get() != null) {
throw Exceptions.propagate(exception.get());
}
}
你可以这样使用它:
Observer<String> sideEffects = new Observer<String>() {
@Override
public void onCompleted() {
System.out.println("onCompleted");
}
@Override
public void onError(Throwable e) {
System.out.println("onError: " + e.getMessage());
}
@Override
public void onNext(String s) {
System.out.println("onNext: " + s);
}
};
runSynchronously(
Observable.just("London", "Paris", "Berlin")
.doOnEach(sideEffects)
);
答案 2 :(得分:1)
在大多数情况下,最好不要阻止方法。尝试尽可能无阻塞。如果您需要在Observable完成后执行某些操作,只需从onCompleted
回调调用您的逻辑,或使用doOnCompleted
运算符。
如果你确实需要阻止,那么你可以使用Blocking Observable Operators
。
大多数运算符都会阻塞,直到Observable完成。
但是,最后可以通过一个简单的System.in.read()
来阻止主要方法过早退出。