如何在Retrofit与Kotlin中从API调用函数返回响应体并在Fragment中使用
如何在Retrofit2(Kotlin)中封装API调用并返回响应体到Fragment
我明白你现在的困扰——Retrofit的enqueue是异步执行的,你没法直接在回调里return数据,因为函数在回调触发前就已经执行完毕了。下面给你两种常用的解决方案,都是Kotlin开发里很实用的方式:
方案一:使用高阶函数(回调模式)
这是最直接的适配方案,我们给API函数添加成功/失败的回调参数,让Fragment在调用时能拿到返回的数据:
修改后的API封装函数
fun getRandomComputerDetails( context: Context, onSuccess: (RandomComputerApiResponse) -> Unit, onError: (String) -> Unit ) { if (isNetworkAvailable(context)) { val retrofit: Retrofit = Retrofit.Builder() .baseUrl(BuildConfig.API_BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() val service: RandomComputerApiService = retrofit.create(RandomComputerApiService::class.java) val listCall: Call<RandomComputerApiResponse> = service.getRandomComputer() listCall.enqueue(object : Callback<RandomComputerApiResponse> { override fun onResponse( call: Call<RandomComputerApiResponse>, response: Response<RandomComputerApiResponse> ) { if (response.isSuccessful) { val randomComputerList: RandomComputerApiResponse? = response.body() randomComputerList?.let(onSuccess) // 成功时回调传递数据 } else { val errorMsg = when (response.code()) { 400 -> "Bad Connection" 404 -> "Not Found" 500 -> "Server Error" else -> "Generic Error" } onError(errorMsg) // 保留原有错误弹窗逻辑 val errorResId = when (response.code()) { 400 -> R.string.dialog_error_400 404 -> R.string.dialog_error_404 500 -> R.string.dialog_error_500 else -> R.string.dialog_error_generic_error } errorDialogHandler(context, errorResId) } } override fun onFailure(call: Call<RandomComputerApiResponse>, t: Throwable) { val errorMsg = t.message ?: "Unknown Error" onError(errorMsg) Log.e("Response error", errorMsg) } }) } else { val errorMsg = context.getString(R.string.dialog_error_no_internet_connection) onError(errorMsg) errorDialogHandler(context, R.string.dialog_error_no_internet_connection) } }
Fragment中的调用示例
// 在Fragment的onViewCreated等生命周期方法中调用 getRandomComputerDetails(requireContext(), onSuccess = { computerList -> // 这里拿到数据后即可更新UI,比如给RecyclerView设置数据源 Log.i("Data received", "$computerList") // updateUI(computerList) }, onError = { errorMsg -> // 可添加额外错误处理逻辑,弹窗已经在封装函数中触发 Log.e("API Error", errorMsg) } )
方案二:使用Kotlin协程(挂起函数,更简洁)
Kotlin协程可以让异步代码看起来像同步代码,是现代Kotlin开发的首选方式。我们需要把API函数改成挂起函数,用await()(或直接返回响应体)替代enqueue。
准备工作
确保你的项目依赖了Retrofit的协程支持(较新版本的Retrofit已内置),同时将API接口方法改为挂起函数:
interface RandomComputerApiService { @GET("your-api-endpoint") suspend fun getRandomComputer(): Response<RandomComputerApiResponse> // 也可以直接返回数据类,此时请求失败会自动抛出异常,需自行捕获 // suspend fun getRandomComputer(): RandomComputerApiResponse }
修改后的API封装函数(挂起函数)
suspend fun getRandomComputerDetails(context: Context): Result<RandomComputerApiResponse> { return try { if (!isNetworkAvailable(context)) { errorDialogHandler(context, R.string.dialog_error_no_internet_connection) return Result.failure(Exception(context.getString(R.string.dialog_error_no_internet_connection))) } val retrofit: Retrofit = Retrofit.Builder() .baseUrl(BuildConfig.API_BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build() val service: RandomComputerApiService = retrofit.create(RandomComputerApiService::class.java) // 挂起协程直到请求完成 val response = service.getRandomComputer() if (response.isSuccessful) { val computerList = response.body() computerList?.let { Result.success(it) } ?: Result.failure(Exception("Response body is null")) } else { val errorMsg = when (response.code()) { 400 -> "Bad Connection" 404 -> "Not Found" 500 -> "Server Error" else -> "Generic Error" } val errorResId = when (response.code()) { 400 -> R.string.dialog_error_400 404 -> R.string.dialog_error_404 500 -> R.string.dialog_error_500 else -> R.string.dialog_error_generic_error } errorDialogHandler(context, errorResId) Result.failure(Exception(errorMsg)) } } catch (t: Throwable) { Log.e("Response error", t.message.toString()) Result.failure(t) } }
Fragment中的调用示例(协程作用域内)
// 在Fragment的生命周期协程中调用,确保跟随Fragment生命周期 viewLifecycleOwner.lifecycleScope.launch { val result = getRandomComputerDetails(requireContext()) result.onSuccess { computerList -> // 拿到数据后更新UI Log.i("Data received", "$computerList") // updateUI(computerList) }.onFailure { error -> // 处理错误逻辑 Log.e("API Error", error.message ?: "Unknown error") } }
方案选择
- 如果你需要快速适配现有代码,不需要引入协程相关知识,高阶函数方案更适合你;
- 如果你想写出更简洁、易维护的现代Kotlin代码,协程方案是更好的选择,它能避免回调嵌套的问题。
内容的提问来源于stack exchange,提问作者Melom




