Retrofit的onResponse回调中无法给成员变量mData赋值如何解决
问题根因
你遇到的问题本质是异步请求的时序问题:
call.enqueue()是异步方法,发起网络请求后会立刻返回,不会阻塞当前线程,loadData方法不会等待onResponse/onFailure回调执行,就会先走return mData.toString()的逻辑,此时mData还没被赋值,自然返回的是null。- 等后续网络请求完成,
onResponse里的mData = 20才会执行,但此时loadData早就返回结果了,所以你感知不到赋值生效。
解决方法
方案1:使用接口回调传递结果
这是Java/Kotlin开发里最常用的传统方案,自定义回调接口,等异步请求完成后把结果回传给调用方:
首先定义回调接口:
interface WeatherLoadCallback { fun onSuccess(data: Int, temp: String) fun onFailure(errorMsg: String?) }
然后改造loadData方法,去掉返回值,传入回调参数:
class ApiConnect { private val BASE_URL = "https://www.metaweather.com" // 不需要提前定义mData存储结果,直接通过回调传递即可 fun loadData(woeidID: String, callback: WeatherLoadCallback) { val retrofit = Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() val service = retrofit.create(WeatherApi::class.java) val call = service.queryWeather(woeidID) call.enqueue(object : Callback<Example> { override fun onResponse(call: Call<Example>, response: Response<Example>) { if (response.isSuccessful) { response.body()?.let { val detay = it.consolidatedWeather val cevap = detay!![0].theTemp.toString() Log.i("woeid", "iç: $cevap") // 回调返回结果 callback.onSuccess(20, cevap) return@let } callback.onFailure("响应体为空") } else { callback.onFailure("请求失败,错误码:${response.code()}") } } override fun onFailure(call: Call<Example>, t: Throwable) { Log.i("response", "onFailure: " + t.message) callback.onFailure(t.message) } }) } }
调用示例:
ApiConnect().loadData("你的woeid", object : WeatherLoadCallback { override fun onSuccess(data: Int, temp: String) { // 此处拿到结果,更新UI或者做后续处理 Log.i("result", "data: $data, temp: $temp") } override fun onFailure(errorMsg: String?) { // 处理错误逻辑 } })
方案2:使用Kotlin协程(Kotlin环境推荐使用)
如果你的项目已经接入Kotlin协程,可以直接将回调式API转为挂起函数,代码更简洁:
首先给WeatherApi里的queryWeather方法加上suspend修饰符,去掉Call包装:
interface WeatherApi { @GET("你的接口路径") suspend fun queryWeather(@Path("woeid") woeidID: String): Example }
改造loadData为挂起函数:
class ApiConnect { private val BASE_URL = "https://www.metaweather.com" private val retrofit = Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() private val service = retrofit.create(WeatherApi::class.java) suspend fun loadData(woeidID: String): Result<Pair<Int, String>> { return try { val response = service.queryWeather(woeidID) val detay = response.consolidatedWeather val cevap = detay!![0].theTemp.toString() Result.success(20 to cevap) } catch (e: Exception) { Result.failure(e) } } }
调用示例(在ViewModel里用viewModelScope,或者Activity里用lifecycleScope):
lifecycleScope.launch { val result = ApiConnect().loadData("你的woeid") result.onSuccess { (data, temp) -> Log.i("result", "data: $data, temp: $temp") }.onFailure { Log.e("result", "请求失败", it) } }
方案3:阻塞等待请求结果(不推荐,会阻塞线程,禁止在主线程使用)
如果你非要同步获取结果,可以用call.execute()方法,但是必须放在子线程执行,否则会触发主线程网络请求报错:
fun loadData(woeidID: String): String { val retrofit = Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() val service = retrofit.create(WeatherApi::class.java) val call = service.queryWeather(woeidID) // 同步执行,阻塞当前线程 val response = call.execute() return if (response.isSuccessful) { response.body()?.let { val detay = it.consolidatedWeather detay!![0].theTemp.toString() 20.toString() } ?: "null" } else { "null" } }
注意:该方案会阻塞线程,只适合特殊场景使用,日常开发优先选方案1或方案2。
内容的提问来源于stack exchange,提问作者ERTUGRUL KOC
相关产品推荐
相关产品推荐

