Android使用Room、LiveData与ViewModel时数据无限更新如何解决?
问题根源
- 死循环的核心原因:Room返回的
LiveData<UserModel>是可观察数据源,只要对应行的数据发生更新,就会自动向所有观察者推送最新数据。你在observe的回调中直接修改用户数据并执行更新操作,更新操作又触发LiveData推送新值,再次进入回调,形成无限循环。 - 错误的观察者注册:
onStop方法中执行observe操作完全不符合生命周期逻辑,onStop是Activity退至后台时的回调,此时注册观察者不仅毫无意义,还会导致每次触发onStop都新增一个观察者,加剧内存泄漏和逻辑混乱。 - 初始化逻辑位置错误:ViewModel、Repository的初始化逻辑写在
onStart中,每次Activity从后台回到前台都会重复执行初始化,完全没必要,应该放到onCreate中执行一次即可。 - 数据修改不规范:直接修改LiveData返回的查询对象实例,容易引发不可预期的问题,建议拷贝对象后再修改属性。
修复方案
第一步:新增一次性查询方法
给DAO新增不需要监听的一次性查询方法,避免不必要的数据变化监听:
@Dao interface UserDAO { // 原有方法保留 @Query("select * from user_table where _id =:id") suspend fun getUserByIdOnce(id : String) : UserModel? }
在Repository和ViewModel中依次新增对应调用:
// Repository新增 suspend fun getUserByIdOnce(id: String) = userDAO.getUserByIdOnce(id) // ViewModel新增 suspend fun getUserByIdOnce(id: String) = userRepository.getUserByIdOnce(id)
第二步:重构MainActivity逻辑
将初始化移到onCreate,删除所有不必要的LiveData监听,改用一次性查询修改状态:
class MainActivity : AppCompatActivity() { private lateinit var viewModel: UserViewModel private lateinit var binding: ActivityMainBinding private lateinit var auth : FirebaseAuth private var userId = "" override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) // 初始化逻辑仅执行一次 val navController = findNavController(R.id.fragmentHome) binding.bottomNavigationHome.setupWithNavController(navController) val userAPI = UserAPI.getInstance() val userRepository = UserRepository(userAPI,this) viewModel = ViewModelProvider(this, UserModelFactory(userRepository)).get(UserViewModel::class.java) auth = Firebase.auth } override fun onStart() { super.onStart() val currentUser = auth.currentUser ?: return userId = currentUser.uid lifecycleScope.launch { val userFromDAO = viewModel.getUserByIdOnce(userId) ?: return@launch // 只有状态不对时才执行更新,避免无效操作 if (!userFromDAO.active) { // 拷贝对象后修改,不改动原查询实例 val updatedUser = userFromDAO.copy(active = true) viewModel.updateUserDAO(updatedUser) Log.e(TAG, updatedUser.toString()) } } } override fun onStop() { super.onStop() Log.e(TAG,"Stop") val currentUser = auth.currentUser ?: return userId = currentUser.uid lifecycleScope.launch { val userFromDAO = viewModel.getUserByIdOnce(userId) ?: return@launch if (userFromDAO.active) { val updatedUser = userFromDAO.copy(active = false) viewModel.updateUserDAO(updatedUser) viewModel.updateUserAPI(updatedUser) } } } }
如果你确实有监听用户数据变化的其他需求,需要保留LiveData观察的话,只需要在观察者回调中增加状态判断,只有当数据真的需要修改时才执行更新操作,不要每次回调都无脑更新即可。
内容的提问来源于stack exchange,提问作者Lộc Trần Phước
相关产品推荐
相关产品推荐

