使用 Retrofit 2 和 RxJava 的 concatMap() 中的 NetworkOnMainThreadException
NetworkOnMainThreadException in concatMap() with Retrofit 2 and RxJava
在我的应用程序中,我尝试使用 RxJava 在用户搜索新电影并从网络服务检索电影数据时做出反应。
为了做到这一点,我想使用 concatMap 将获取新电影查询的操作连接到向网络服务请求电影。
这样做我得到一个 NetworkOnMainThread 异常,我不明白原因..
createSearchViewObservable(searchView)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.concatMap(new Func1<String, Observable<MoviesWrapper>>() {
@Override
public Observable<MoviesWrapper> call(String title) {
RestMovieSource repo = new RestMovieSource();
return repo.searchMovieByTitle(title);
}
})
.subscribe(new Subscriber<MoviesWrapper>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
Toast.makeText(getContext(), e.getCause().getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onNext(MoviesWrapper moviesWrapper) {
for (Movie movie : moviesWrapper.getResults()) {
Toast.makeText(getContext(), movie.getTitle(), Toast.LENGTH_SHORT).show();
}
}
});
D/OkHttp: --> GET /3/search/movie?api_key=xxxx&query=Jurassic%20World HTTP/1.1
D/OkHttp: --> END GET
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1147)
at java.net.InetAddress.lookupHostByName(InetAddress.java:418)
at java.net.InetAddress.getAllByNameImpl(InetAddress.java:252)
at java.net.InetAddress.getAllByName(InetAddress.java:215)
at com.squareup.okhttp.Dns.lookup(Dns.java:39)
at com.squareup.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:184)
at com.squareup.okhttp.internal.http.RouteSelector.nextProxy(RouteSelector.java:153)
at com.squareup.okhttp.internal.http.RouteSelector.next(RouteSelector.java:95)
at com.squareup.okhttp.internal.http.HttpEngine.createNextConnection(HttpEngine.java:345)
at com.squareup.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:328)
at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:246)
at com.squareup.okhttp.Call.getResponse(Call.java:276)
at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:234)
at com.squareup.okhttp.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:180)
at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:223)
at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:196)
at com.squareup.okhttp.Call.execute(Call.java:79)
at retrofit.OkHttpCall.execute(OkHttpCall.java:116)
at retrofit.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:111)
at retrofit.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:88)
at rx.Observable.call(Observable.java:162)
at rx.Observable.call(Observable.java:154)
at rx.Observable.call(Observable.java:162)
at rx.Observable.call(Observable.java:154)
at rx.Observable.unsafeSubscribe(Observable.java:8171)
at rx.internal.operators.OperatorConcat$ConcatSubscriber.subscribeNext(OperatorConcat.java:172)
at rx.internal.operators.OperatorConcat$ConcatSubscriber.onNext(OperatorConcat.java:136)
at rx.internal.operators.OperatorConcat$ConcatSubscriber.onNext(OperatorConcat.java:79)
at rx.internal.operators.OperatorMap.onNext(OperatorMap.java:54)
at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.pollQueue(OperatorObserveOn.java:202)
at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.call(OperatorObserveOn.java:162)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
只需在 concatMap()
:
之后调用 subscribeOn()
createSearchViewObservable(searchView)
.concatMap(new Func1<String, Observable<MoviesWrapper>>() {
@Override
public Observable<MoviesWrapper> call(String title) {
RestMovieSource repo = new RestMovieSource();
return repo.searchMovieByTitle(title);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<MoviesWrapper>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
Toast.makeText(getContext(), e.getCause().getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onNext(MoviesWrapper moviesWrapper) {
for (Movie movie : moviesWrapper.getResults()) {
Toast.makeText(getContext(), movie.getTitle(), Toast.LENGTH_SHORT).show();
}
}
});
我解决了为从 concatMap() 返回的可观察对象定义 subscribeOn 和 observeOn 的问题。外面的 subscribeOn/observeOn 仅特定于来自 createSearchViewObservable(searchView) 的第一个可观察对象。
createSearchViewObservable(searchView)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.concatMap(new Func1<String, Observable<MoviesWrapper>>() {
@Override
public Observable<MoviesWrapper> call(String title) {
RestMovieSource repo = new RestMovieSource();
return repo.searchMovieByTitle(title)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());;
}
})
.subscribe(...)
ConcatMap 或 flatMap 将生成一个新的 observable,它从旧的 observable 获取数据。
因此,您将调度程序设置为旧的可观察对象,而不是新的。
在我的应用程序中,我尝试使用 RxJava 在用户搜索新电影并从网络服务检索电影数据时做出反应。 为了做到这一点,我想使用 concatMap 将获取新电影查询的操作连接到向网络服务请求电影。 这样做我得到一个 NetworkOnMainThread 异常,我不明白原因..
createSearchViewObservable(searchView)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.concatMap(new Func1<String, Observable<MoviesWrapper>>() {
@Override
public Observable<MoviesWrapper> call(String title) {
RestMovieSource repo = new RestMovieSource();
return repo.searchMovieByTitle(title);
}
})
.subscribe(new Subscriber<MoviesWrapper>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
Toast.makeText(getContext(), e.getCause().getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onNext(MoviesWrapper moviesWrapper) {
for (Movie movie : moviesWrapper.getResults()) {
Toast.makeText(getContext(), movie.getTitle(), Toast.LENGTH_SHORT).show();
}
}
});
D/OkHttp: --> GET /3/search/movie?api_key=xxxx&query=Jurassic%20World HTTP/1.1
D/OkHttp: --> END GET
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1147)
at java.net.InetAddress.lookupHostByName(InetAddress.java:418)
at java.net.InetAddress.getAllByNameImpl(InetAddress.java:252)
at java.net.InetAddress.getAllByName(InetAddress.java:215)
at com.squareup.okhttp.Dns.lookup(Dns.java:39)
at com.squareup.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:184)
at com.squareup.okhttp.internal.http.RouteSelector.nextProxy(RouteSelector.java:153)
at com.squareup.okhttp.internal.http.RouteSelector.next(RouteSelector.java:95)
at com.squareup.okhttp.internal.http.HttpEngine.createNextConnection(HttpEngine.java:345)
at com.squareup.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:328)
at com.squareup.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:246)
at com.squareup.okhttp.Call.getResponse(Call.java:276)
at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:234)
at com.squareup.okhttp.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:180)
at com.squareup.okhttp.Call$ApplicationInterceptorChain.proceed(Call.java:223)
at com.squareup.okhttp.Call.getResponseWithInterceptorChain(Call.java:196)
at com.squareup.okhttp.Call.execute(Call.java:79)
at retrofit.OkHttpCall.execute(OkHttpCall.java:116)
at retrofit.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:111)
at retrofit.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:88)
at rx.Observable.call(Observable.java:162)
at rx.Observable.call(Observable.java:154)
at rx.Observable.call(Observable.java:162)
at rx.Observable.call(Observable.java:154)
at rx.Observable.unsafeSubscribe(Observable.java:8171)
at rx.internal.operators.OperatorConcat$ConcatSubscriber.subscribeNext(OperatorConcat.java:172)
at rx.internal.operators.OperatorConcat$ConcatSubscriber.onNext(OperatorConcat.java:136)
at rx.internal.operators.OperatorConcat$ConcatSubscriber.onNext(OperatorConcat.java:79)
at rx.internal.operators.OperatorMap.onNext(OperatorMap.java:54)
at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.pollQueue(OperatorObserveOn.java:202)
at rx.internal.operators.OperatorObserveOn$ObserveOnSubscriber.call(OperatorObserveOn.java:162)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
只需在 concatMap()
:
subscribeOn()
createSearchViewObservable(searchView)
.concatMap(new Func1<String, Observable<MoviesWrapper>>() {
@Override
public Observable<MoviesWrapper> call(String title) {
RestMovieSource repo = new RestMovieSource();
return repo.searchMovieByTitle(title);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<MoviesWrapper>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
Toast.makeText(getContext(), e.getCause().getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onNext(MoviesWrapper moviesWrapper) {
for (Movie movie : moviesWrapper.getResults()) {
Toast.makeText(getContext(), movie.getTitle(), Toast.LENGTH_SHORT).show();
}
}
});
我解决了为从 concatMap() 返回的可观察对象定义 subscribeOn 和 observeOn 的问题。外面的 subscribeOn/observeOn 仅特定于来自 createSearchViewObservable(searchView) 的第一个可观察对象。
createSearchViewObservable(searchView)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.concatMap(new Func1<String, Observable<MoviesWrapper>>() {
@Override
public Observable<MoviesWrapper> call(String title) {
RestMovieSource repo = new RestMovieSource();
return repo.searchMovieByTitle(title)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());;
}
})
.subscribe(...)
ConcatMap 或 flatMap 将生成一个新的 observable,它从旧的 observable 获取数据。 因此,您将调度程序设置为旧的可观察对象,而不是新的。