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, чтобы продолжать использовать ее. Это скороговорка, которую мы используем во всех примерах.

Так что просто сохраняйте ссылку на жилтаты, возвращаемые комнатой, а не просто наблюдайте за ними, теперь все работает хорошо.

Другие вопросы по тегам