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

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")
            }
        })
}

问题根源

  1. 异步请求时序问题:Retrofit的enqueue是异步执行的,调用callLoginApi后会立即执行后续的if (sessionKey.isNotEmpty())判断,此时API请求还未完成,sessionKey仍为空,所以首次点击不触发跳转。
  2. 重复点击未限制:未对按钮点击做防重复处理,再次点击时,第一次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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 07:37:11