Android 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

