在GC之后不会触发Room返回的实时数据 [英] Livedata returned by Room won't be triggered after GC
问题描述
我们的团队正在利用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:我们现在正在使用Flowable
在onNext()
中发射实时数据来解决此问题,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.
推荐答案
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屋!