在GC之后不会触发Room返回的实时数据 [英] Livedata returned by Room won't be triggered after GC

查看:124
本文介绍了在GC之后不会触发Room返回的实时数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们的团队正在利用Android Jetpack的优势来构建一个项目.

Our team are building a project with benefits from Android Jetpack.

有些演示代码显示了我们面临的问题.这些代码可以在 https://github.com/viseator/TestRoomLivedata

There are demo code showing the question we are facing. These code can be found at https://github.com/viseator/TestRoomLivedata

我创建一个UserDao:

@Dao
interface UserDao {
    @Query("SELECT * FROM user WHERE uid = :uid LIMIT 1")
    fun findUserById(uid: String?): Single<User>

    @Query("SELECT * FROM user WHERE state = 1 LIMIT 1")
    fun findLoginUserWithObserve(): LiveData<User>

    @Query("SELECT * FROM user WHERE state =1 LIMIT 1")
    fun findLoginUser(): Single<User>

    @Update
    fun update(vararg user: User)
}

我还创建了一个kotlin对象来管理用户的状态.

I also created a kotlin object to manage user's state.

我正在观察findLoginUserWithObserve()返回的实时数据,以便在登录用户更改时得到通知:

I'm observing the livedata returned by findLoginUserWithObserve() to get notified when login user changed:

object AccountManager {
    private const val DATA_BASE_NAME = "users"
    val TAG = "AccountManager"
    fun init(context: Application) {
        sDb = databaseBuilder(context, UserDataBase::class.java, DATA_BASE_NAME).build()
        sDao = sDb.userDao()
        sDao.findLoginUserWithObserve().observeForever {
            Log.d(TAG, "notified: $it")
        }
    }

    private lateinit var sDb: UserDataBase
    private lateinit var sDao: UserDao

    fun findLoginUserWithObserve() = sDao.findLoginUserWithObserve()

    fun logoutFlowable(): Single<Boolean> = sDao.findLoginUser().subscribeOn(
            Schedulers.io()).map { user ->
        user.state = User.STATE_NOT_LOGIN
        sDao.update(user)
        true
    }

    fun login(user: User) = logoutFlowable().subscribe({ doLogin(user) }, { doLogin(user) })

    private fun doLogin(user: User) = sDao.findUserById(user.uid).subscribeOn(
            Schedulers.io()).subscribe({ origin ->
        origin.userName = user.userName
        origin.state = User.STATE_HAVE_LOGIN
        sDao.update(origin)
        user.state = User.STATE_HAVE_LOGIN
    }, {
        user.state = User.STATE_HAVE_LOGIN
        sDao.insert(user)
    })

}

我通过调用init方法初始化Applicaiton中的AccountManager并创建演示活动:

I initialize the AccountManager in the Applicaiton by calling it's init method and create a demo activity:

class MainActivity : AppCompatActivity() {
    private var i = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        AccountManager.login(User().apply {
            userName = "user1"
            uid = System.currentTimeMillis().toString()
        })
        button.setOnClickListener {
            AccountManager.login(User().apply {
                userName = "user${i++}"
                uid = System.currentTimeMillis().toString()
            })
        }
    }
}

我想一旦调用AccountManager.login(),我将得到通知,并打印一条日志消息.但是我们发现,GC之后我们将不会再收到通知. (我们通过Android Studio Profiler触发GC)

I suppose once AccountManager.login() be called, I will get notified and it will print a log message. But we found that we won't be notified anymore after GC. (We trigger GC by Android Studio Profiler)

日志消息

在探索由room生成的UserDao_Impl类之后,我们发现它创建了一个观察者并通过调用addWeakObserver()与数据库链接:

After exploring the UserDao_Impl class generated by room, we found it create a observer and link with database by calling addWeakObserver():

  @Override
  public LiveData<User> findLoginUserWithObserve() {
    final String _sql = "SELECT * FROM user WHERE state = 1 LIMIT 1";
    final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
    return new ComputableLiveData<User>(__db.getQueryExecutor()) {
      private Observer _observer;

      @Override
      protected User compute() {
        if (_observer == null) {
          _observer = new Observer("user") {
            @Override
            public void onInvalidated(@NonNull Set<String> tables) {
              invalidate();
            }
          };
          __db.getInvalidationTracker().addWeakObserver(_observer);
        }

所以我们想知道为什么这里的房间使用WeakObserver使得房间返回的实时数据不可靠?

So We wonder why room using WeakObserver here, which makes livedata returned by room unreliable?

PS:我们现在正在使用FlowableonNext()中发射实时数据来解决此问题,onNext()将每次按预期触发.

PS: We are using Flowable to emit livedata in it's onNext() now to work around this, onNext() will be triggered every time as expected.

推荐答案

将此问题发布到google问题跟踪器后(

After post this issue to the google issue tracker(https://issuetracker.google.com/issues/114833188), I got reply:

如果不想再使用LiveData,我们不想泄漏它.我们可以 从技术上讲,当LiveData处于输入状态时,会不断添加和删除观察者 使用/未使用;但这可能意味着错过一些发生的事件 当LiveData处于非活动状态时.我们最初是这样做的 原型,但变得难以维护.你应该留个参考 到LiveData继续使用它.这是我们所有使用的模式 例子.

We don't want to leak the LiveData if it is not used anymore. We could technically keep adding and removing the observer when LiveData is in use / not in use; but that might mean missing some events that happens when LiveData is inactive. We used to do that in the initial prototypes but became harder to maintain. You should keep a reference to the LiveData to keep using it. This is the patter we use in all examples.

因此,只要保留对房间返回的实时数据的引用,而不是仅仅观察它,一切就可以正常工作了.

So just keep a reference to the livedata returned by room instead of just observing it, everything works well now.

这篇关于在GC之后不会触发Room返回的实时数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆