Android rxJava 错误处理与改造
Android rxJava Error handling with retrofit
我正在使用较新的 RX java 而不是
Observable.create(new Observable.OnSubscribeFunc<T>() {...});
这是使用的:(由于弃用)
Observable.create(new Observable.OnSubscribe<T>() {...});
(这可能很重要,因为大多数示例、教程、探索都使用旧的...)
好吧,让我们看看我的问题。我有一个 Java 类,其中的相关部分:
private interface ApiManagerService {
@FormUrlEncoded
@POST("/login")
User getUser(@Field("username") String userName, @Field("password") String password);
}
private static RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(HOST)
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();
private static ApiManagerService apiManager = restAdapter.create(ApiManagerService.class);
public static Subscription login(final String userName, final String password, Observer<User> observer) {
return Observable.create(new Observable.OnSubscribe<User>() {
@Override
public void call(Subscriber<? super User> subscriber) {
try {
User user = apiManager.getUser(userName, password);
subscriber.onNext(user);
subscriber.onCompleted();
} catch (RetrofitError e) {
subscriber.onError(e);
} catch (Throwable e) {
subscriber.onError(e);
}
}
}
).subscribeOn(Schedulers.io())
.retry(3)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
如果一切正常,这段代码几乎可以完美运行。但是如果我犯了一个故意的错误,比如我关闭了 WiFi .. 而不是改造得到 "UnKnownHostException"... 因为它应该发生在改造调用 (getUser) 中try catch 块。但不是将错误处理到 onError(Throwable t) --> 我可以处理的地方,它只是使应用程序崩溃。所以这就像错误永远不会到达 catch 块一样。
奇怪的是,HTTP 错误(如 404、401 等)被捕获,由 onError(...) 得到,一切都很好。
从 .retry(3) 开始,一切都在崩溃前进行了 3 次,但没有一次进入 catch 子句。
编辑 1
LogCat 输出:
01-08 16:19:31.576 15285-16162/asd.bdef.gh D/Retrofit﹕ ---- ERROR https://testapi.com/api/login
01-08 16:19:31.606 15285-16162/asd.bdef.gh D/Retrofit﹕ java.net.UnknownHostException: Unable to resolve host "testapi.com": No address associated with hostname
at java.net.InetAddress.lookupHostByName(InetAddress.java:394)
at java.net.InetAddress.getAllByNameImpl(InetAddress.java:236)
at java.net.InetAddress.getAllByName(InetAddress.java:214)
at com.squareup.okhttp.internal.Network.resolveInetAddresses(Network.java:29)
at com.squareup.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:259)
at com.squareup.okhttp.internal.http.RouteSelector.nextProxy(RouteSelector.java:233)
at com.squareup.okhttp.internal.http.RouteSelector.nextUnconnected(RouteSelector.java:159)
at com.squareup.okhttp.internal.http.RouteSelector.next(RouteSelector.java:133)
at com.squareup.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:314)
at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:237)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:423)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:105)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:239)
at com.squareup.okhttp.internal.huc.DelegatingHttpsURLConnection.getOutputStream(DelegatingHttpsURLConnection.java:218)
at com.squareup.okhttp.internal.huc.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:25)
at retrofit.client.UrlConnectionClient.prepareRequest(UrlConnectionClient.java:68)
at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.java:37)
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:321)
at retrofit.RestAdapter$RestHandler.access0(RestAdapter.java:220)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:265)
at retrofit.RxSupport.run(RxSupport.java:55)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:390)
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at retrofit.Platform$Android.run(Platform.java:142)
at java.lang.Thread.run(Thread.java:841)
01-08 16:19:31.606 15285-16162/asd.bdef.gh D/Retrofit﹕ ---- END ERROR
01-08 16:19:31.977 15285-15285/asd.bdef.gh D/AndroidRuntime﹕ Shutting down VM
01-08 16:19:31.977 15285-15285/asd.bdef.gh W/dalvikvm﹕ threadid=1: thread exiting with uncaught exception (group=0x41c9d8b0)
01-08 16:19:31.977 15285-15285/asd.bdef.gh E/AndroidRuntime﹕ FATAL EXCEPTION: main
给定的api地址不是真实地址,但真实地址是可达的。我刚刚关闭了 WiFi 以测试错误处理。
还有一个用例:如果我添加到 observable .onExceptionResumeNext([2nd observable]) 而不是它转到第二个 observable,它不会崩溃。但这不是解决问题的方法。
编辑 2
ApiManager.login(userName, pass, new Observer<User>() {
@Override
public void onCompleted() { }
@Override
public void onError(Throwable e) {
DialogManager.showBasicErrorDialog(getApplicationContext(), e.getLocalizedMessage());
logger.showLog("Login Not ok");
e.printStackTrace();
}
@Override
public void onNext(User user) {
logger.showLog("login ok, user: " + user.getName().toString());
{...}
}
});
编辑 3
FATAL EXCEPTION: main
rx.exceptions.OnErrorFailedException: Error occurred when trying to propagate error to Observer.onError
at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:175)
at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:97)
at rx.internal.operators.NotificationLite.accept(NotificationLite.java:144)
{...}
Caused by: java.lang.NullPointerException: Attempt to read from field 'rx.functions.Action0 rx.schedulers.TrampolineScheduler$TimedAction.action' on a null object reference
at rx.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.enqueue(TrampolineScheduler.java:85)
在此先感谢您的帮助。
你不必使用 Retrofit 构建自己的 Observable,因为 Retrofit 可以直接 return Observable:
http://square.github.io/retrofit/
Retrofit also integrates RxJava to support methods with a return type
of rx.Observable
@GET("/user/{id}/photo") Observable getUserPhoto(@Path("id")
int id);
(您不必自己处理错误)
你能post你崩溃的堆栈跟踪吗?正如我和您的想法一样,您的应用程序不应该崩溃。
看起来你可能 运行 一个问题已经在 RxJava 1.0 中得到修复:
我正在使用较新的 RX java 而不是
Observable.create(new Observable.OnSubscribeFunc<T>() {...});
这是使用的:(由于弃用)
Observable.create(new Observable.OnSubscribe<T>() {...});
(这可能很重要,因为大多数示例、教程、探索都使用旧的...)
好吧,让我们看看我的问题。我有一个 Java 类,其中的相关部分:
private interface ApiManagerService {
@FormUrlEncoded
@POST("/login")
User getUser(@Field("username") String userName, @Field("password") String password);
}
private static RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(HOST)
.setLogLevel(RestAdapter.LogLevel.FULL)
.build();
private static ApiManagerService apiManager = restAdapter.create(ApiManagerService.class);
public static Subscription login(final String userName, final String password, Observer<User> observer) {
return Observable.create(new Observable.OnSubscribe<User>() {
@Override
public void call(Subscriber<? super User> subscriber) {
try {
User user = apiManager.getUser(userName, password);
subscriber.onNext(user);
subscriber.onCompleted();
} catch (RetrofitError e) {
subscriber.onError(e);
} catch (Throwable e) {
subscriber.onError(e);
}
}
}
).subscribeOn(Schedulers.io())
.retry(3)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
如果一切正常,这段代码几乎可以完美运行。但是如果我犯了一个故意的错误,比如我关闭了 WiFi .. 而不是改造得到 "UnKnownHostException"... 因为它应该发生在改造调用 (getUser) 中try catch 块。但不是将错误处理到 onError(Throwable t) --> 我可以处理的地方,它只是使应用程序崩溃。所以这就像错误永远不会到达 catch 块一样。 奇怪的是,HTTP 错误(如 404、401 等)被捕获,由 onError(...) 得到,一切都很好。
从 .retry(3) 开始,一切都在崩溃前进行了 3 次,但没有一次进入 catch 子句。
编辑 1
LogCat 输出:
01-08 16:19:31.576 15285-16162/asd.bdef.gh D/Retrofit﹕ ---- ERROR https://testapi.com/api/login
01-08 16:19:31.606 15285-16162/asd.bdef.gh D/Retrofit﹕ java.net.UnknownHostException: Unable to resolve host "testapi.com": No address associated with hostname
at java.net.InetAddress.lookupHostByName(InetAddress.java:394)
at java.net.InetAddress.getAllByNameImpl(InetAddress.java:236)
at java.net.InetAddress.getAllByName(InetAddress.java:214)
at com.squareup.okhttp.internal.Network.resolveInetAddresses(Network.java:29)
at com.squareup.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:259)
at com.squareup.okhttp.internal.http.RouteSelector.nextProxy(RouteSelector.java:233)
at com.squareup.okhttp.internal.http.RouteSelector.nextUnconnected(RouteSelector.java:159)
at com.squareup.okhttp.internal.http.RouteSelector.next(RouteSelector.java:133)
at com.squareup.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:314)
at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:237)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:423)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.java:105)
at com.squareup.okhttp.internal.huc.HttpURLConnectionImpl.getOutputStream(HttpURLConnectionImpl.java:239)
at com.squareup.okhttp.internal.huc.DelegatingHttpsURLConnection.getOutputStream(DelegatingHttpsURLConnection.java:218)
at com.squareup.okhttp.internal.huc.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:25)
at retrofit.client.UrlConnectionClient.prepareRequest(UrlConnectionClient.java:68)
at retrofit.client.UrlConnectionClient.execute(UrlConnectionClient.java:37)
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:321)
at retrofit.RestAdapter$RestHandler.access0(RestAdapter.java:220)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:265)
at retrofit.RxSupport.run(RxSupport.java:55)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:390)
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at retrofit.Platform$Android.run(Platform.java:142)
at java.lang.Thread.run(Thread.java:841)
01-08 16:19:31.606 15285-16162/asd.bdef.gh D/Retrofit﹕ ---- END ERROR
01-08 16:19:31.977 15285-15285/asd.bdef.gh D/AndroidRuntime﹕ Shutting down VM
01-08 16:19:31.977 15285-15285/asd.bdef.gh W/dalvikvm﹕ threadid=1: thread exiting with uncaught exception (group=0x41c9d8b0)
01-08 16:19:31.977 15285-15285/asd.bdef.gh E/AndroidRuntime﹕ FATAL EXCEPTION: main
给定的api地址不是真实地址,但真实地址是可达的。我刚刚关闭了 WiFi 以测试错误处理。
还有一个用例:如果我添加到 observable .onExceptionResumeNext([2nd observable]) 而不是它转到第二个 observable,它不会崩溃。但这不是解决问题的方法。
编辑 2
ApiManager.login(userName, pass, new Observer<User>() {
@Override
public void onCompleted() { }
@Override
public void onError(Throwable e) {
DialogManager.showBasicErrorDialog(getApplicationContext(), e.getLocalizedMessage());
logger.showLog("Login Not ok");
e.printStackTrace();
}
@Override
public void onNext(User user) {
logger.showLog("login ok, user: " + user.getName().toString());
{...}
}
});
编辑 3
FATAL EXCEPTION: main
rx.exceptions.OnErrorFailedException: Error occurred when trying to propagate error to Observer.onError
at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:175)
at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:97)
at rx.internal.operators.NotificationLite.accept(NotificationLite.java:144)
{...}
Caused by: java.lang.NullPointerException: Attempt to read from field 'rx.functions.Action0 rx.schedulers.TrampolineScheduler$TimedAction.action' on a null object reference
at rx.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.enqueue(TrampolineScheduler.java:85)
在此先感谢您的帮助。
你不必使用 Retrofit 构建自己的 Observable,因为 Retrofit 可以直接 return Observable:
http://square.github.io/retrofit/
Retrofit also integrates RxJava to support methods with a return type of rx.Observable
@GET("/user/{id}/photo") Observable getUserPhoto(@Path("id") int id);
(您不必自己处理错误)
你能post你崩溃的堆栈跟踪吗?正如我和您的想法一样,您的应用程序不应该崩溃。
看起来你可能 运行 一个问题已经在 RxJava 1.0 中得到修复: