You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Retrofit+RxJava加载API数据到RecyclerView时遇NetworkOnMainThreadException

解决Retrofit+RxJava出现NetworkOnMainThreadException的问题

兄弟,我一眼就看到你代码里的坑了——你重复订阅了同一个Observable,而且第二次订阅的是完全没经过线程切换处理的原始对象,这直接导致网络请求跑在了主线程上,触发了那个讨厌的异常。

先看你代码里的问题点:

Observable<MoviesResponse> call = apiService.getTopRatedMovies(API_KEY);
// 这里做了线程切换,但只是调用subscribe()却没处理结果,链式调用的返回值被直接丢弃了
call.subscribeOn(Schedulers.newThread())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(); 
// 重点:你又订阅了原始的call!这个订阅完全没应用上面的线程配置,直接在主线程发起网络请求
call.subscribe(new Observer<MoviesResponse>() { ... });

正确的写法应该是这样

把线程切换和订阅逻辑合并成一个链式调用,确保你的Observable是经过线程配置后再被订阅的:

ApiInterface apiService = ApiService.getClient().create(ApiInterface.class);
Observable<MoviesResponse> movieObservable = apiService.getTopRatedMovies(API_KEY);

// 链式调用到底,一次性完成线程配置+订阅
movieObservable.subscribeOn(Schedulers.io()) // 推荐用io线程池,比newThread()更高效,能复用线程
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(new Observer<MoviesResponse>() {
        @Override
        public void onSubscribe(Disposable d) {
            // 这里可以加一些订阅开始的逻辑,比如显示加载Toast或者进度条
            Toast.makeText(getApplicationContext(), "正在请求数据...", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onNext(MoviesResponse moviesResponse) {
            // 拿到数据后直接更新RecyclerView
            if (moviesResponse != null && moviesResponse.getResults() != null) {
                // 假设你有一个提前初始化好的适配器
                yourMovieAdapter.setMovieList(moviesResponse.getResults());
                yourMovieAdapter.notifyDataSetChanged();
            }
        }

        @Override
        public void onError(Throwable e) {
            // 处理请求失败的情况
            Toast.makeText(getApplicationContext(), "请求出错:" + e.getMessage(), Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onComplete() {
            // 请求完成的收尾逻辑,比如隐藏加载框
            Toast.makeText(getApplicationContext(), "数据加载完成", Toast.LENGTH_SHORT).show();
        }
    });

额外的几个小建议

  1. 别用Schedulers.newThread():换成Schedulers.io(),它是专门为IO操作设计的线程池,会自动复用线程,比每次新建线程性能更好。
  2. 避免重复订阅Observable:RxJava的冷Observable每次订阅都会重新执行一次请求,不仅浪费资源,还会像你这样出现线程问题。
  3. 如果你的项目支持Java 8+,可以用Lambda简化代码,看起来更清爽:
movieObservable.subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(
        // onNext回调
        moviesResponse -> {
            yourMovieAdapter.setMovieList(moviesResponse.getResults());
            yourMovieAdapter.notifyDataSetChanged();
        },
        // onError回调
        throwable -> Toast.makeText(getApplicationContext(), "请求失败:" + throwable.getMessage(), Toast.LENGTH_SHORT).show(),
        // onComplete回调
        () -> Toast.makeText(getApplicationContext(), "加载完成", Toast.LENGTH_SHORT).show()
    );

内容的提问来源于stack exchange,提问作者Tom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 02:24:17