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

Kotlin协程中如何将suspend挂起函数的返回值存储到变量?

问题根源

你当前的login方法内部使用CoroutineScope(Dispatchers.IO).launch启动异步协程,launch不会阻塞调用它的主线程,login方法不会等待协程内的网络请求执行完成,就直接返回了初始化的空字符串resp,所以拿到的返回值永远为空。

修改方案

第一步:修改ApiUtil的login方法

将login改为suspend挂起函数,不需要内部单独创建协程作用域,直接挂起等待网络请求完成后返回结果,同时修正代码中baseUrl引用错误的问题:

class ApiUtil {

    companion object {
        var API_BASE_URL = "https://localhost:8100/testApi"

        fun getInterceptor() : OkHttpClient {
            val logging = HttpLoggingInterceptor()
            logging.level = HttpLoggingInterceptor.Level.BODY
            val okHttpClient = OkHttpClient.Builder()
                .addInterceptor(logging)
                .build()
            return  okHttpClient
        }

        fun createService() : ApiInterface {
            val retrofit = Retrofit.Builder()
                .client(getInterceptor())           
                .addConverterFactory(GsonConverterFactory.create())
                // 修正此处的baseUrl引用错误
                .baseUrl(API_BASE_URL)
                .build()
            return retrofit.create(ApiInterface::class.java)
        }
    }

    // 改为suspend函数,返回可空String处理请求失败的场景
    suspend fun login(userParam: UserParam): String? {
        val gson = Gson()
        val json = gson.toJson(userParam)
        val requestBody = json.toString().toRequestBody("application/json".toMediaTypeOrNull())
        
        // 切换到IO线程执行请求,自动挂起等待结果
        return withContext(Dispatchers.IO) {
            val response = createService().login(requestBody)
            if (response.isSuccessful){
                val gson = GsonBuilder().setPrettyPrinting().create()
                val prettyJson = gson.toJson(
                    JsonParser.parseString(
                        response.body()?.string()
                    )
                )
                Log.d("Pretty Printed JSON :", prettyJson)
                // 直接返回结果
                prettyJson
            } else {
                Log.e("RETROFIT_ERROR", response.code().toString())
                // 请求失败返回null
                null
            }
        }
    }
}

第二步:修改LoginActivity的调用逻辑

suspend函数需要在协程中调用,这里使用Android官方提供的lifecycleScope启动协程,和Activity生命周期绑定避免内存泄漏:

class LoginActivity : AppCompatActivity() {
    // 提前声明控件变量
    private lateinit var edtMobileNo: EditText
    private lateinit var edtPassword: EditText
    private lateinit var btnLogin: Button
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 注意补充设置布局的代码
        setContentView(R.layout.activity_login)
        
        edtMobileNo = findViewById(R.id.edtUsername)
        edtPassword = findViewById(R.id.edtPassword)
        btnLogin = findViewById(R.id.btnLogin)
        
        btnLogin.setOnClickListener {
            val api = ApiUtil()
            val userParam = UserParam(edtMobileNo.text.toString(), edtPassword.text.toString())
            // 使用lifecycleScope启动协程
            lifecycleScope.launch {
                // 等待login方法返回结果
                val respJson = api.login(userParam)
                val msg = when {
                    respJson == null -> "请求失败,请稍后重试"
                    JSONObject(respJson).getString("message") == "OK" -> "Login OK"
                    else -> "Login failed"
                }
                // lifecycleScope默认运行在主线程,可直接操作UI
                Toast.makeText(applicationContext, msg, Toast.LENGTH_SHORT).show()
            }
        }
    }
}

注意事项

如果项目中还未引入lifecycle协程扩展,需要在模块级build.gradle的dependencies中添加依赖:

implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.2"

版本可替换为和你项目compileSdk适配的版本。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 14:24:02