Livedata, возвращенные Room, не будут активированы после GC - PullRequest
0 голосов
/ 11 сентября 2018

Наша команда разрабатывает проект с преимуществами 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 для управления состоянием пользователя.

I 'Наблюдая за живыми данными, возвращаемыми 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 для излученияВ «liveata» теперь onNext(), чтобы обойти это, onNext() будет запускаться каждый раз, как и ожидалось.

1 Ответ

0 голосов
/ 12 сентября 2018

После публикации этой проблемы в системе отслеживания проблем Google (https://issuetracker.google.com/issues/114833188), я получил ответ:

Мы не хотим утекать LiveData, если он больше не используется. Мы могли бы технически продолжайте добавлять и удалять наблюдателя, когда LiveData находится в использовать / не использовать; но это может означать пропустить некоторые события, которые происходят когда LiveData неактивен. Мы привыкли делать это в начальной прототипы, но стало сложнее поддерживать. Вы должны сохранить ссылку к LiveData, чтобы продолжать использовать его. Это скороговорка, которую мы используем во всех примеры.

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

...