Android跨Fragment获取ViewModel的LiveData值为null的解决方法
问题分析
你碰到的核心问题是ViewModel实例不共享:
- 当你在
AddressBookFragment里用by viewModels()获取AuthViewModel时,会生成一个属于当前Fragment(或者说所属HomeActivity)的全新实例,和登录时那个Fragment里的AuthViewModel完全是两个独立对象。 - 再加上你的业务流程:登录成功后跳转到了
HomeActivity,原来的登录Activity大概率会被销毁,它对应的AuthViewModel也会跟着被回收,所以新实例里的getUserResponseMutable自然是空的。
解决方案
根据你跨Activity传递登录数据的场景,推荐以下几种可行方案:
方案一:通过Intent传递数据(简单直接)
如果你的GetUserResponse类可以序列化,直接把数据通过Intent传给HomeActivity,再在Fragment中获取:
- 让
GetUserResponse实现Serializable接口:data class GetUserResponse(/* 你的字段 */) : Serializable - 登录成功后,把数据放入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() } - 在
HomeActivity中接收数据,并存入Activity级别的ViewModel:
先创建HomeViewModel:
然后在HomeActivity的@HiltViewModel class HomeViewModel @ViewModelInject constructor() : ViewModel() { val userResponseLiveData = MutableLiveData<GetUserResponse>() }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 } } - 最后在
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):
- 登录成功后,把
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) } - 在
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一致:
- 创建App级ViewModel:
@HiltViewModel class AppViewModel @ViewModelInject constructor(application: Application) : AndroidViewModel(application) { val userResponseLiveData = MutableLiveData<GetUserResponse>() } - 在登录的
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) } } - 在
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
相关产品推荐
相关产品推荐

