Kotlin中按钮调用API后无法跳转至目标Fragment的问题求助
登录按钮点击跳转异常问题分析与解决
问题现象
首次点击登录按钮时,能成功获取Session Key,但无法跳转到目标Fragment;再次点击按钮时,会执行跳转操作,但会重复调用API并生成新的Session Key。
原代码片段
onCreateView中的点击事件
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { binding = DataBindingUtil.inflate(inflater, R.layout.fragment_login, container, false) val view = binding.root sharedViewModel.setAppVersion(BuildConfig.VERSION_NAME) sharedViewModel.setFlavor(BuildConfig.FLAVOR) binding.apply { viewModel = loginViewModel } binding.apply { paramViewModel = sharedViewModel } with(binding) { loginButton.setOnClickListener { it if (checkUsernamePassword()) { callLoginApi(username.text.toString(), password.text.toString()) if (sessionKey.isNotEmpty()) { Navigation.findNavController(it) .navigate(R.id.action_loginFragment_to_landingPageFragment) } } } } return view }
登录API调用代码
private fun callLoginApi(_username: String, _password: String) { val login = LoginRequest(_username, _password) val retrofit = ServiceBuilder.buildService(RestApiInterface::class.java) retrofit.sendReq(login).enqueue( object : retrofit2.Callback<LoginResponse> { override fun onResponse( call: Call<LoginResponse>, response: Response<LoginResponse> ) { if (response.body()?.sessionKey?.isNotEmpty() == true) { sessionKey = response.body()?.sessionKey.toString() Toast.makeText( activity, response.body()?.sessionKey, Toast.LENGTH_LONG ).show() //sharedViewModel.setSessionKey(response.body()?.sessionKey.toString()) } else Toast.makeText( activity, response.body()?.message, Toast.LENGTH_LONG ).show() } override fun onFailure(call: Call<LoginResponse>, t: Throwable) { TODO("Not yet implemented") } }) }
问题根源
- 异步请求时序问题:Retrofit的
enqueue是异步执行的,调用callLoginApi后会立即执行后续的if (sessionKey.isNotEmpty())判断,此时API请求还未完成,sessionKey仍为空,所以首次点击不触发跳转。 - 重复点击未限制:未对按钮点击做防重复处理,再次点击时,第一次API请求已返回
sessionKey,此时会触发跳转,但同时会再次调用API生成新的Session Key。
解决方案
1. 将跳转逻辑移至API成功回调内
确保只有在成功获取Session Key后才执行跳转操作。
2. 添加按钮防重复点击处理
点击后禁用按钮,请求完成(成功/失败)后恢复按钮状态,避免多次调用API。
修改后的代码
点击事件修改
with(binding) { loginButton.setOnClickListener { if (checkUsernamePassword()) { // 点击后禁用按钮,防止重复请求 loginButton.isEnabled = false callLoginApi(username.text.toString(), password.text.toString()) } } }
API调用代码修改
private fun callLoginApi(_username: String, _password: String) { val login = LoginRequest(_username, _password) val retrofit = ServiceBuilder.buildService(RestApiInterface::class.java) retrofit.sendReq(login).enqueue( object : retrofit2.Callback<LoginResponse> { override fun onResponse( call: Call<LoginResponse>, response: Response<LoginResponse> ) { // 请求完成后恢复按钮状态 binding.loginButton.isEnabled = true if (response.body()?.sessionKey?.isNotEmpty() == true) { sessionKey = response.body()?.sessionKey.toString() Toast.makeText( activity, response.body()?.sessionKey, Toast.LENGTH_LONG ).show() // 成功获取Session Key后执行跳转 Navigation.findNavController(binding.root) .navigate(R.id.action_loginFragment_to_landingPageFragment) // 建议启用该行,将SessionKey存入ViewModel,便于全局使用 // sharedViewModel.setSessionKey(sessionKey) } else { Toast.makeText( activity, response.body()?.message ?: "登录失败,请重试", Toast.LENGTH_LONG ).show() } } override fun onFailure(call: Call<LoginResponse>, t: Throwable) { // 请求失败也恢复按钮状态 binding.loginButton.isEnabled = true Toast.makeText(activity, "网络请求失败: ${t.message}", Toast.LENGTH_LONG).show() } }) }
额外建议
- 建议将
sessionKey存入sharedViewModel(启用注释的代码行),而不是存在Fragment的成员变量中,这样更符合MVVM架构,也便于其他页面获取Session Key。 - 可以添加加载状态提示(比如显示ProgressBar),提升用户体验。
内容的提问来源于stack exchange,提问作者Dean Ball
相关产品推荐
相关产品推荐

