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

Android Retrofit调用异常:OnDataCallback为空致先返回Null列表问题求助

解决Retrofit调用中OnDataCallback为空导致先返回Null的问题

你遇到的核心问题是异步调用的时序不匹配——在call.enqueue()执行时,OnDataCallback还没被正确绑定到Interactor实例上,导致第一次回调触发时拿不到回调对象,只能返回Null;等后续异步请求完成时,Callback已经绑定完毕,所以能正常返回数据。下面给你几个针对性的解决思路:

1. 把Callback绑定提前到Interactor实例初始化阶段

你提到“进入Interactor类并创建自身实例”,大概率是先创建了Interactor,之后才去设置OnDataCallback,但这时候getMovieList()已经被调用了。可以把Callback的绑定直接放到Interactor的构造函数里,从根源上避免空指针:

public class MovieInteractor {
    private final OnDataCallback callback;

    // 构造函数直接传入Callback,确保实例创建时就绑定好
    public MovieInteractor(OnDataCallback callback) {
        this.callback = callback;
    }

    public void getMovieList() {
        Call<MovieResponse> call = apiService.getMovies();
        call.enqueue(new Callback<MovieResponse>() {
            @Override
            public void onResponse(Call<MovieResponse> call, Response<MovieResponse> response) {
                // 这里无需额外判空,因为构造时已经确保callback存在
                callback.onSuccess(response.body().getMovieList());
            }

            @Override
            public void onFailure(Call<MovieResponse> call, Throwable t) {
                callback.onError(t.getMessage());
            }
        });
    }
}

然后在Presenter的getData()方法里,创建Interactor时直接传入Callback:

public void getData() {
    MovieInteractor interactor = new MovieInteractor(new OnDataCallback() {
        @Override
        public void onSuccess(List<Movie> movieList) {
            // 通知View层更新数据
            view.renderMovieList(movieList);
        }

        @Override
        public void onError(String error) {
            view.showErrorToast(error);
        }
    });
    interactor.getMovieList();
}

2. 避免Interactor实例重复创建

如果你的Presenter中多次创建了Interactor实例,可能会出现:第一次创建的实例没绑定Callback就调用了getMovieList(),而后续绑定Callback的是另一个全新的实例。这种情况下要确保整个请求流程只使用同一个Interactor实例,比如在Presenter初始化时就创建好:

public class MoviePresenter {
    private MovieView view;
    private MovieInteractor interactor;

    public MoviePresenter(MovieView view) {
        this.view = view;
        // 初始化阶段就绑定好Callback,后续直接复用这个实例
        this.interactor = new MovieInteractor(new OnDataCallback() {
            @Override
            public void onSuccess(List<Movie> movieList) {
                view.renderMovieList(movieList);
            }

            @Override
            public void onError(String error) {
                view.showErrorToast(error);
            }
        });
    }

    public void getData() {
        // 直接调用已绑定Callback的interactor实例
        interactor.getMovieList();
    }
}

3. 给Callback添加兜底的非空校验

虽然最好从根源解决Callback为空的问题,但为了避免崩溃和方便排查,可以在Interactor中给Callback的调用加上非空判断,同时打印日志:

call.enqueue(new Callback<MovieResponse>() {
    @Override
    public void onResponse(Call<MovieResponse> call, Response<MovieResponse> response) {
        if (callback != null) {
            callback.onSuccess(response.body().getMovieList());
        } else {
            Log.e("MovieInteractor", "OnDataCallback为空,无法回调成功结果");
        }
    }

    @Override
    public void onFailure(Call<MovieResponse> call, Throwable t) {
        if (callback != null) {
            callback.onError(t.getMessage());
        } else {
            Log.e("MovieInteractor", "OnDataCallback为空,无法回调错误信息: " + t.getMessage());
        }
    }
});

关键提醒:绑定Callback必须在发起请求之前

Retrofit的enqueue()是异步执行,但它的回调对象是在调用enqueue()时就确定的。如果你先调用getMovieList()发起请求,之后才去设置Callback,那当异步请求完成时,第一次的回调还是会因为Callback为空而无法正确通知Presenter。所以绑定Callback的操作一定要在调用getMovieList()之前完成。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:57:31