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

Android跨Fragment获取ViewModel的LiveData值为null的解决方法

问题分析

你碰到的核心问题是ViewModel实例不共享:

  • 当你在AddressBookFragment里用by viewModels()获取AuthViewModel时,会生成一个属于当前Fragment(或者说所属HomeActivity)的全新实例,和登录时那个Fragment里的AuthViewModel完全是两个独立对象。
  • 再加上你的业务流程:登录成功后跳转到了HomeActivity,原来的登录Activity大概率会被销毁,它对应的AuthViewModel也会跟着被回收,所以新实例里的getUserResponseMutable自然是空的。
解决方案

根据你跨Activity传递登录数据的场景,推荐以下几种可行方案:

方案一:通过Intent传递数据(简单直接)

如果你的GetUserResponse类可以序列化,直接把数据通过Intent传给HomeActivity,再在Fragment中获取:

  1. 让GetUserResponse实现Serializable接口:
    data class GetUserResponse(/* 你的字段 */) : Serializable
    
  2. 登录成功后,把数据放入Intent再跳转:
    if (getUserResponse.result){
        isSuccessfull.value=true
        getUserResponseMutable.value=getUserResponse
        // 把数据放入Intent
        val intent = Intent(view.context,HomeActivity::class.java)
        intent.putExtra("USER_RESPONSE", getUserResponse)
        view.context.startActivity(intent)
        // 可选:销毁登录Activity,防止用户返回登录页
        (view.context as Activity).finish()
    }
    
  3. 在HomeActivity中接收数据,并存入Activity级别的ViewModel:
    先创建HomeViewModel:
    @HiltViewModel
    class HomeViewModel @ViewModelInject constructor() : ViewModel() {
        val userResponseLiveData = MutableLiveData<GetUserResponse>()
    }
    
    然后在HomeActivity的onCreate中接收数据:
    private val homeViewModel: HomeViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        intent.getSerializableExtra("USER_RESPONSE")?.let {
            homeViewModel.userResponseLiveData.value = it as GetUserResponse
        }
    }
    
  4. 最后在AddressBookFragment中获取Activity级别的ViewModel并观察:
    private val homeViewModel: HomeViewModel by activityViewModels()
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        homeViewModel.userResponseLiveData.observe(viewLifecycleOwner) { userResponse ->
            if (userResponse.result) {
                Toast.makeText(requireContext(), "用户数据已获取", Toast.LENGTH_SHORT).show()
            }
        }
    }
    

方案二:持久化存储(适合长期保存的数据)

如果这个用户数据需要在App重启后依然存在,或者跨多个页面频繁使用,建议把数据存在本地(比如SharedPreferences或者Jetpack DataStore):

  1. 登录成功后,把GetUserResponse转成JSON字符串存入SharedPreferences:
    // 在AuthViewModel的登录成功逻辑里
    if (getUserResponse.result){
        isSuccessfull.value=true
        getUserResponseMutable.value=getUserResponse
        // 保存到SharedPreferences
        val gson = Gson()
        val userJson = gson.toJson(getUserResponse)
        val sharedPref = view.context.getSharedPreferences("AppSettings", Context.MODE_PRIVATE)
        sharedPref.edit().putString("SAVED_USER_RESPONSE", userJson).apply()
        // 跳转页面
        val intent = Intent(view.context,HomeActivity::class.java)
        view.context.startActivity(intent)
    }
    
  2. 在AddressBookFragment中读取并解析数据:
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        val sharedPref = requireContext().getSharedPreferences("AppSettings", Context.MODE_PRIVATE)
        val userJson = sharedPref.getString("SAVED_USER_RESPONSE", null)
        userJson?.let {
            val gson = Gson()
            val userResponse = gson.fromJson(it, GetUserResponse::class.java)
            if (userResponse.result) {
                Toast.makeText(requireContext(), "asdadasd", Toast.LENGTH_SHORT).show()
            }
        }
    }
    

方案三:应用级ViewModel(全局共享)

如果你需要在整个App的任何组件中都能访问这个用户数据,可以使用Application级别的ViewModel,它的生命周期和App一致:

  1. 创建App级ViewModel:
    @HiltViewModel
    class AppViewModel @ViewModelInject constructor(application: Application) : AndroidViewModel(application) {
        val userResponseLiveData = MutableLiveData<GetUserResponse>()
    }
    
  2. 在登录的AuthViewModel中注入并更新数据:
    class AuthViewModel @ViewModelInject constructor (
        // 其他依赖
        private val appViewModel: AppViewModel
    ):ViewModel() {
        // ... 其他代码
        if (getUserResponse.result){
            isSuccessfull.value=true
            getUserResponseMutable.value=getUserResponse
            // 更新AppViewModel的数据
            appViewModel.userResponseLiveData.value = getUserResponse
            // 跳转页面
            val intent = Intent(view.context,HomeActivity::class.java)
            view.context.startActivity(intent)
        }
    }
    
  3. 在AddressBookFragment中获取AppViewModel并观察:
    private val appViewModel: AppViewModel by viewModels({ requireActivity().application })
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        appViewModel.userResponseLiveData.observe(viewLifecycleOwner) { userResponse ->
            if (userResponse.result) {
                Toast.makeText(requireContext(), "asdadasd", Toast.LENGTH_SHORT).show()
            }
        }
    }
    
额外优化建议

你的getUser函数里用了Dispatchers.Main,但网络请求应该放在IO线程执行,建议修正:

suspend fun getUser(
    id: Int,
    authorization : String
)= withContext(Dispatchers.IO){getUserRepository.getUser(id,authorization)}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 14:37:36