Retrofit2中全局List变量赋值后返回null的技术咨询
问题分析与解决方案:异步请求导致返回null
兄弟,你这个问题我太熟了——这完全是异步请求的执行顺序坑到你了!咱们来拆解一下代码的执行流程,你就明白为啥返回null了:
- 你调用
retrieveDogsFromREST()方法后,代码会立刻发起异步网络请求(enqueue()方法就是把请求放到后台线程去跑) - 主线程可不会等后台的网络请求完成,它会直接跳过
onResponse回调,执行后面的Log.d("onRetrieveDR", ""+ responseDogs),这时候responseDogs还没被赋值,可不就是null吗? - 等网络请求成功返回后,
onResponse才会被调用,这时候responseDogs才被赋值,但这时候retrieveDogsFromREST()早就已经把null返回给调用方了!
说白了就是:你想在同步方法里直接拿异步请求的结果,这根本行不通——异步操作的结果不会立刻就绪。
下面给你几种常用的解决方案,你可以根据项目情况选:
方案1:用自定义回调接口(最基础的原生方案)
咱们不用方法返回值,而是定义一个回调接口,等请求成功后再把结果传回来:
首先定义回调接口:
public interface DogRetrievalCallback { void onSuccess(List<Dog> dogs); void onFailure(int errorCode); }
然后修改你的retrieveDogsFromREST方法,把回调传进去:
public void retrieveDogsFromREST(DogRetrievalCallback callback) { final DogService dogService = DogService.serviceDog; dogService.getDogs(AuthRequest.createAuthJsonString()).enqueue(new Callback<DogList>() { @Override public void onResponse(Call<DogList> call, Response<DogList> response) { if (response.isSuccessful()) { List<Dog> dogs = response.body().getDogs(); Log.d("DogRESTSuccessful", "" + dogs); callback.onSuccess(dogs); // 请求成了,把结果给回调 } else { Log.d("DogRESTNSuccessful", "- statusCode" + response.code()); callback.onFailure(response.code()); // 失败了传错误码 } } @Override public void onFailure(Call<DogList> call, Throwable t) { callback.onFailure(-1); // 网络异常传个自定义错误码 t.printStackTrace(); } }); }
调用的时候这么用:
dogREST.retrieveDogsFromREST(new DogRetrievalCallback() { @Override public void onSuccess(List<Dog> dogs) { // 这里就能拿到非空的dogs列表了,想干嘛干嘛(比如更新UI) } @Override public void onFailure(int errorCode) { // 处理错误情况,比如弹个Toast提示用户 } });
方案2:用LiveData(适合Android Jetpack项目)
如果你的项目用了Android架构组件,用LiveData来观察数据变化会更优雅,还能自动处理生命周期:
先把responseDogs改成LiveData类型:
private MutableLiveData<List<Dog>> responseDogs = new MutableLiveData<>(); public LiveData<List<Dog>> retrieveDogsFromREST() { final DogService dogService = DogService.serviceDog; dogService.getDogs(AuthRequest.createAuthJsonString()).enqueue(new Callback<DogList>() { @Override public void onResponse(Call<DogList> call, Response<DogList> response) { if (response.isSuccessful()) { List<Dog> dogs = response.body().getDogs(); Log.d("DogRESTSuccessful", "" + dogs); responseDogs.postValue(dogs); // 后台线程更新LiveData要用postValue } else { Log.d("DogRESTNSuccessful", "- statusCode" + response.code()); responseDogs.postValue(null); } } @Override public void onFailure(Call<DogList> call, Throwable t) { responseDogs.postValue(null); t.printStackTrace(); } }); return responseDogs; }
然后在Activity/Fragment里观察这个LiveData:
dogREST.retrieveDogsFromREST().observe(this, new Observer<List<Dog>>() { @Override public void onChanged(List<Dog> dogs) { if (dogs != null) { // 拿到数据,处理业务逻辑 } else { // 处理错误 } } });
方案3:用Coroutines(Kotlin/Java都支持,代码更简洁)
如果项目用了协程(Java可以用kotlinx-coroutines-java库),可以把异步请求写成同步风格的代码,不用写回调:
Kotlin示例(挂起函数):
suspend fun retrieveDogsFromREST(): List<Dog>? { return try { val dogService = DogService.serviceDog val response = dogService.getDogs(AuthRequest.createAuthJsonString()).await() if (response.isSuccessful) { response.body()?.dogs } else { Log.d("DogRESTNSuccessful", "- statusCode" + response.code()) null } } catch (e: Exception) { e.printStackTrace() null } }
调用的时候要在协程作用域里:
lifecycleScope.launch { val dogs = dogREST.retrieveDogsFromREST() if (dogs != null) { // 处理数据 } }
Java的话可以用runBlocking或者CoroutineScope的Java API,不过Kotlin协程的写法会更清爽。
总之核心就是:异步操作的结果不能在发起请求后立刻获取,必须用回调、LiveData、协程这类异步处理方式来接收结果,不能像同步代码那样直接返回变量。
内容的提问来源于stack exchange,提问作者Iago Brayham
相关产品推荐
相关产品推荐

