Livedata, возвращенные Room, не будут активированы после GC
Наша команда строит проект с преимуществами Android Jetpack.
Есть демонстрационный код, показывающий вопрос, с которым мы сталкиваемся. Этот код можно найти по адресу 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 для управления состоянием пользователя.
Я наблюдаю за возвращенными findLoginUserWithObserve()
чтобы получить уведомление при смене имени пользователя:
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)
})
}
Я инициализирую AccountManager
в Applicaiton
называя это init
Метод и создать демонстрационную активность:
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. (Мы запускаем GC от Android Studio Profiler)
После изучения UserDao_Impl
класс, сгенерированный комнатой, мы обнаружили, что он создает наблюдателя и связывается с базой данных, вызывая 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
здесь, что делает жилатата, возвращенный комнатой ненадежным?
PS: мы используем Flowable
излучать ливата в это onNext()
Теперь, чтобы обойти это, onNext()
будет срабатывать каждый раз, как и ожидалось.
1 ответ
После публикации этой проблемы в системе отслеживания проблем Google ( https://issuetracker.google.com/issues/114833188) я получил ответ:
Мы не хотим утекать LiveData, если он больше не используется. Мы могли бы технически продолжать добавлять и удалять наблюдателя, когда LiveData используется / не используется; но это может означать пропуск некоторых событий, которые происходят, когда LiveData неактивен. Мы делали это в первоначальных прототипах, но их стало сложнее поддерживать. Вы должны сохранить ссылку на LiveData, чтобы продолжать использовать ее. Это скороговорка, которую мы используем во всех примерах.
Так что просто сохраняйте ссылку на жилтаты, возвращаемые комнатой, а не просто наблюдайте за ними, теперь все работает хорошо.