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
相关产品推荐
相关产品推荐

