LiveData List не обновляется при обновлении базы данных

В настоящее время я занимаюсь рефакторингом устаревшего кода для использования компонентов архитектуры Android и настраиваю запросы базы данных и залпов комнаты в своего рода шаблоне репозитория. Таким образом, уровень представления / домена просит репозиторий заставить LiveData-Objects наблюдать или сказать ему о синхронизации с сервером, после чего старые записи базы данных удаляются, а все текущие записи загружаются с сервера.

Я написал тесты для части синхронизации, поэтому я уверен, что объекты извлекаются и вставляются в базу данных правильно. Но при написании теста для наблюдения за записями этой таблицы db (и проверки правильности сохранения объектов со всем, что нужно сделать, прежде чем помещать их в db) LiveData>, за которым я наблюдаю, не запускается.

В следующем фрагменте кода вы можете предположить, что метод synchronizeFormsWithServer (...) работает правильно и выполняет операции с базой данных асинхронно. Он содержит операции, которые удаляют все объекты формы из базы данных, которых нет в списке форм, полученных с сервера, и вставляют все новые. Поскольку в начале теста база данных пуста, это не имеет большого значения.

Тест, в котором наблюдатель не запускается:

  @Test
  public void shouldSaveFormsFromServerIntoDb() throws Exception
   {
    Lifecycle lifecycle = Mockito.mock(Lifecycle.class);
    when(lifecycle.getCurrentState()).thenReturn(Lifecycle.State.RESUMED);
    LifecycleOwner owner = Mockito.mock(LifecycleOwner.class);
    when(owner.getLifecycle()).thenReturn(lifecycle);

    final CountDownLatch l = new CountDownLatch(19);

    formRepository.allForms().observe(owner, formList ->
    {
     if (formList != null && formList.isEmpty())
      {
       for (Form form : formList)
        {
         testForm(form);
         l.countDown();
        }
      }
    });

    formRepository.synchronizeFormsWithServer(owner);
    l.await(2, TimeUnit.MINUTES);
    assertEquals(0, l.getCount());
   }

Код FormRepository:

  @Override
  public LiveData<List<Form>> allForms()
   {
    return formDatastore.getAllForms();
   }

Хранилище данных:

  @Override
  public LiveData<List<Form>> getAllForms()
   {
    return database.formDao().getAllForms();
   }

Код formDao (база данных реализована так, как вы ожидаете от комнаты):

  @Query("SELECT * FROM form")
  LiveData<List<Form>> getAllForms();

Вполне возможно, что я чего-то не понимал в компонентах LiveData, потому что я использую их впервые, так что, возможно, я что-то в корне ошибся.

Мы очень ценим любую помощь :)

PS: я наткнулся на ЭТО сообщение, в котором обсуждается аналогичная проблема, но поскольку я в настоящее время вообще не использую DI и использую только один экземпляр formrepository (с которым связан только один экземпляр formDao), я не думаю, что это та же проблема.


person Antonio Dell    schedule 31.08.2017    source источник


Ответы (3)


Хорошо, я нашел решение, хотя не знаю, почему он так себя ведет.

Помните, я сказал: «Не беспокойтесь о методе синхронизации»? Что ж ... Оказывается, с этим было несколько неправильных вещей, которые еще больше задержали решение.

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

@Update
void update(Form form)

в dao, который по неизвестным причинам не запускает LiveData-Observer. Поэтому я изменил его на

@Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(Form form);

После этого я мог получить Form-LiveData из моего репозитория так же просто, как

LiveData<List<Form>> liveData = formRepository.allForms();

Тогда подпишитесь на нее как обычно. Ранее неудачный тест теперь выглядит так:

  @Test
  public void shouldSaveFormsFromServerIntoDb() throws Exception
   {
    Lifecycle lifecycle = Mockito.mock(Lifecycle.class);
    when(lifecycle.getCurrentState()).thenReturn(Lifecycle.State.RESUMED);
    LifecycleOwner owner = Mockito.mock(LifecycleOwner.class);
    when(owner.getLifecycle()).thenReturn(lifecycle);

    final CountDownLatch l = new CountDownLatch(19);

    final SortedList<Form> sortedForms = new SortedList<Form>(Form.class, new SortedList.Callback<Form>()
     {
      @Override
      public int compare(Form o1, Form o2)
       {
        return o1.getUniqueId().compareTo(o2.getUniqueId());
       }


      @Override
      public void onChanged(int position, int count)
       {
        Log.d(LOG_TAG, "onChanged: Form at position " + position + " has changed. Count is " + count);
        for (int i = 0; i < count; i++)
         {
          l.countDown();
         }
       }


      @Override
      public boolean areContentsTheSame(Form oldItem, Form newItem)
       {
        return (oldItem.getContent() != null && newItem.getContent() != null && oldItem.getContent().equals(newItem.getContent())) || oldItem.getContent() == null && newItem.getContent() == null;
       }


      @Override
      public boolean areItemsTheSame(Form item1, Form item2)
       {
        return item1.getUniqueId().equals(item2.getUniqueId());
       }


      @Override
      public void onInserted(int position, int count)
       {

       }


      @Override
      public void onRemoved(int position, int count)
       {

       }


      @Override
      public void onMoved(int fromPosition, int toPosition)
       {

       }
     });

    LiveData<List<Form>> ld = formRepository.allForms();
    ld.observe(owner, formList ->
    {
     if (formList != null && !formList.isEmpty())
      {
       Log.d(LOG_TAG, "shouldSaveFormsFromServerIntoDb: List contains " + sortedForms.size() + " Forms");
       sortedForms.addAll(formList);
      }
    });

    formRepository.synchronizeFormsWithServer(owner);
    l.await(2, TimeUnit.MINUTES);
    assertEquals(0, l.getCount());
   }

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

Я не знаю, поможет ли это вам @ joao86, но, возможно, у вас есть аналогичная проблема. Если да, не забудьте прокомментировать здесь :)

person Antonio Dell    schedule 05.09.2017
comment
Ты спас мой день - person Cecil Paul; 01.03.2019

Вы должны использовать один и тот же экземпляр базы данных во всех местах.

=> Используйте для этого синглтон

person Tobias    schedule 17.09.2018
comment
Ага, вот и ответ. @ Update работает, @ Query с запросами UPDATE работают. Просто разные экземпляры базы данных вызывают проблемы. Обратитесь к medium.com/@BladeCoder/ за отличным котлином. сообщение о синглтонах (с контекстом, который нужен для баз данных комнат) - person Carsten Hagemann; 24.02.2019

У меня была аналогичная проблема с вашей -> LiveData не обновляется его значение после первого звонка

Вместо использования LiveData используйте MutableLiveData и передайте объект MutableLiveData<List<Form>> в репозиторий и выполните setValue или postValue нового содержимого списка.

Судя по моему опыту, которого не так много, очевидно, что наблюдатель связан с объектом, который вы также сначала назначаете, и каждое изменение должно производиться с этим объектом.

person joao86    schedule 31.08.2017
comment
Я не решаюсь использовать ваш подход, поскольку это означало бы обернуть все вызовы моего репозитория какой-нибудь асинктной задачей или чем-то подобным, когда Room явно разрешает такие операции в основном потоке, если возвращается LiveData. Прочитав все комментарии / сообщения в чате в вашем вопросе, я нахожу это очень странным, эта комната ведет себя так, как вы думаете, даже если вам кажется, что это так. Если на самом деле нет другого варианта, я приму ваш ответ и сделаю это сюда. Но, возможно, кто-то сможет пролить больше света на проблему и найти способ работы с возвратом LiveData непосредственно из dao :) - person Antonio Dell; 31.08.2017
comment
Мне бы это тоже понравилось, потому что это немного странно, что это так ведет себя :) - person joao86; 31.08.2017
comment
Будет ли этот подход обновлять пользовательский интерфейс, когда какая-то другая служба изменяет значения БД? Потому что да, если вы обновляете значения только при вызове метода репозитория get (...), это не проблема, но если, например, ContentProvider вставляет что-то в базу данных, я не думаю, что пользовательский интерфейс покажет изменения до следующего вызова Time get (...). Говоря о вашем связанном коде вопроса здесь - person Antonio Dell; 31.08.2017
comment
Вы, наверное, правы, но я последовал примеру в developer.android.com/topic /libraries/architecture/guide.html, и вот что они там объяснили. - person joao86; 31.08.2017
comment
Судя по информации, приведенной в этой ссылке, я не думаю, что делаю что-то не так. Думаю, что информации, предоставленной Google, на данный момент недостаточно, возможно, потому, что это все еще альфа. - person joao86; 31.08.2017