Постоянная библиотека для Android - вызовы DAO являются асинхронными, поэтому как получить обратный вызов?

Из того, что я прочитал, Комната не позволяет вам выполнять запросы к базе данных в основном потоке (что может вызвать задержки в основном потоке). поэтому представьте, что я пытаюсь обновить текстовое представление в пользовательском интерфейсе основной поток, который содержит некоторые данные, как мне перезвонить. Позвольте мне показать вам пример. Представьте, что я хочу сохранить данные моей бизнес-модели в объекте под названием Events. Таким образом, у нас будет объект EventDao:

представьте, что у нас есть этот объект DAO ниже:

@Dao
public interface EventDao {

   @Query("SELECT * FROM " + Event.TABLE_NAME + " WHERE " + Event.DATE_FIELD + " > :minDate" limit 1)
   LiveData<List<Event>> getEvent(LocalDateTime minDate);

   @Insert(onConflict = REPLACE)
   void addEvent(Event event);

   @Delete
   void deleteEvent(Event event);

   @Update(onConflict = REPLACE)
   void updateEvent(Event event);

}

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

 myTextView.setText(EventDao.getEvent(someDate));/*i think this is illegal as im trying to call room dao on mainthread, therefore how is this done correctly ? would i need to show a spinner while it updates ?*/

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

Еще немного информации: я хотел использовать базу данных комнат в качестве механизма для получения информации о модели вместо того, чтобы хранить ее статически в памяти. поэтому модель будет доступна мне локально через базу данных после того, как я загрузю ее через службу отдыха.

ОБНОВЛЕНИЕ: так как я возвращаю живые данные, я могу сделать это:

eventDao = eventDatabase.eventDao();
eventDao.getEvent().observe(this, event -> {
     myTextView.setText(event.get(0));
});

и это работает для чего-то очень маленького. но представьте, что в моей базе данных миллион элементов. тогда, когда я сделаю этот вызов, будет задержка при получении данных. В самый первый раз, когда это будет вызвано, пользователю будет видно, что есть задержка. Как этого избежать? Чтобы быть ясным, бывают случаи, когда мне не нужны живые данные, мне просто нужно обновить представление один раз. Мне нужно знать, как это сделать? даже если это не с liveData.


person j2emanue    schedule 31.05.2017    source источник
comment
Вы возвращаете LiveData. Итак, зарегистрируйте наблюдателя за изменениями в LiveData. Это описано в документации для LiveData.   -  person CommonsWare    schedule 31.05.2017
comment
так что оператор SQL Select также вызовет обратный вызов liveData?   -  person j2emanue    schedule 31.05.2017
comment
Эммм ... Думаю, да. Я вижу, к чему вы клоните. Эквиваленты RxJava2, которые поддерживает Dao, возвращают Flowable или Publisher, и вы подписываетесь на них, чтобы получить фактические данные. Я предполагаю, что LiveData работает аналогично. До сих пор работа с Room, которую я проделал, заключалась в модификации образца, который уже использовал AsyncTask; Я не буду играть с LiveData еще пару недель.   -  person CommonsWare    schedule 31.05.2017
comment
@CommonsWare я обновил свой вопрос   -  person j2emanue    schedule 31.05.2017
comment
Как этого избежать? - не запрашивайте миллион элементов. бывают случаи, когда мне не нужны живые данные, мне просто нужно обновить один раз представление - вы можете реализовать метод @Query, который не возвращает LiveData (например, возвращает List<Event> напрямую), и самостоятельно выполнять потоки (например, AsyncTask , IntentService, JobService, Thread и шину событий).   -  person CommonsWare    schedule 31.05.2017
comment
знаете ли вы, существует ли так или иначе для хранения базы данных в памяти, а не на диске? мне нужен более быстрый доступ. в противном случае я подумываю использовать статический метод, чтобы удерживать модель в памяти вместо этого и оставлять место. Я боюсь, что он покажет пользователю задержки в потоке пользовательского интерфейса.   -  person j2emanue    schedule 31.05.2017
comment
знаете ли вы, существует ли так или иначе для хранения базы данных в памяти, а не на диске? - В комнате (и SQLite) есть опция базы данных в памяти, хотя я не вижу ее значения. в противном случае я подумываю об использовании статического метода для хранения модели в памяти вместо этого и освобождения места - ну, ну, смысл Room (и SQLite, и файлов) заключается в том, чтобы сохранять данные. Если вы не сохраните данные, ваши данные в памяти уйдут вместе с процессом. Использование Room (или SQLite, или файлов) не отменяет необходимости, возможно, иметь уровень кеширования в вашем приложении.   -  person CommonsWare    schedule 31.05.2017
comment
вы можете использовать для этого сопрограммы Android.   -  person manmohan    schedule 22.09.2019


Ответы (3)


Если вы хотите выполнять свой запрос синхронно и не получать уведомления об обновлениях в наборе данных, просто не переносите возвращаемое значение в объект LiveData. Посмотрите пример кода от Google.

Взгляните на loadProductSync() здесь

person Bohsen    schedule 13.06.2017
comment
Вы создаете его с помощью: // Создайте базу данных! База данных приложений db = Room.databaseBuilder (context.getApplicationContext (), AppDatabase.class, DATABASE_NAME) .build (); - person Bohsen; 13.06.2017
comment
Пример Google создает базу данных с помощью AsyncTask, чтобы передать работу основному потоку. Взгляните на это: github.com/googlesamples/android-architecture-components/blob/ - person Bohsen; 13.06.2017
comment
но loadProductSyn все еще выполняет работу в другом потоке. комната обеспечивает это. если вы не установите allowMainThreadQueries (), тогда вся работа будет выполняться асинхронно. - person j2emanue; 13.06.2017
comment
Но почему это проблема? Вы хотите, чтобы при срабатывании запроса отображался диалог выполнения? При работе с миллионами строк сначала проиндексируйте столбцы в вашей базе данных, которые вы запрашиваете. Во-вторых, если вы не можете избежать запроса большого набора данных, вам следует подумать о создании представления базы данных и запросить представление. google.dk/ - person Bohsen; 13.06.2017
comment
спасибо, что прояснили это. поэтому у меня были последние сомнения по поводу loadProduct и loadProductSync. Почему Google назвал это синхронизацией, когда база данных комнат выполняет все запросы вне основного потока. для меня разница между двумя DAO: @Query (выберите * из продуктов, где id =: productId) LiveData ‹ProductEntity› loadProduct (int productId); @Query (выберите * из продуктов, где id =: productId) ProductEntity loadProductSync (int productId); заключается в том, что он возвращает liveData, поэтому это означает, что ваш подписчик будет продолжать получать события об изменениях. другой просто возвращает одноразовое событие async ?? - person j2emanue; 13.06.2017
comment
Что ж, вы правы в том, что запрос в loadProductSync выполняется в фоновом потоке, но дело в том, что вы не будете получать обновления в своем наборе данных, когда произойдут изменения. Я только что взял код из образца Google, и этот метод фактически никогда не используется. Интересно, собирались ли они использовать его в примере SyncAdapter, где вы хотите синхронизировать свою базу данных с сервером. Кстати отредактировал мой ответ. - person Bohsen; 15.06.2017
comment
Мне нужно исправить свой последний комментарий, потому что я только что изменил проект BasicSample, чтобы использовать метод loadProductSync. При вызове mProduct = databaseCreator.getDatabase (). ProductDao (). LoadProductSync (mProductId); непосредственно в классе ProductViewModel приложение вылетает с ошибкой «Невозможно получить доступ к базе данных в основном потоке, поскольку это может потенциально заблокировать пользовательский интерфейс». Добавление .allowMainThreadQueries () в построитель базы данных останавливает сбой. Таким образом, это будет означать, что запрос выполняется в основном потоке, что было моим исходным предположением. - person Bohsen; 15.06.2017
comment
Спасибо, Бозен, я был озадачен проектом BasicSample, так как не видел allowMainThreadQueries. Так что же нам остается? Вам либо нужно использовать .observe () и LiveData для получения _id вставленной строки, либо добавить allowMainThreadQueries и обещать использовать их только для коротких элементов, таких как long insert (something_small) ??? - person Mike; 22.04.2018
comment
@Mike Если ваши идентификаторы генерируются автоматически, вы можете легко вернуть их из операции INSERT. Проверьте этот ответ SO. - person Bohsen; 03.05.2018
comment
Метод loadProductSync больше не используется внутри BasicSample. - person p72b; 21.09.2019

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

при создании базы данных вы можете использовать: allowMainThreadQueries ()

и для использования в памяти: Room.inMemoryDatabaseBuilder ()

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

но если вы действительно хотите использовать обратный вызов ... используя rxJava, вот тот, который я сделал для списка стран, которые я хотел сохранить в базе данных:

public Observable<CountryModel> queryCountryInfoFor(final String isoCode) {
    return Observable.fromCallable(new Callable<CountryModel>() {
        @Override
        public CountryModel call() throws Exception {
            return db.countriesDao().getCountry(isoCode);
        }
    }).subscribeOn(Schedulers.io())
       
 .observeOn(AndroidSchedulers.mainThread());

}

затем вы можете легко добавить подписчика к этой функции, чтобы получить обратный вызов с помощью Rxjava.

person j2emanue    schedule 31.05.2017

Как Бозен предложил использовать liveata для запроса синхронно. Но в каком-то особом случае мы хотим выполнить асинхронную операцию, основанную на логике. В приведенном ниже примере мне нужно получить некоторые дочерние комментарии для родительских комментариев. Он уже доступен в БД, но его нужно извлекать на основе его parent_id в адаптере recyclerview. Для этого я использовал концепцию возврата AsyncTask, чтобы вернуть результат. (Возвращение в Котлин)

Класс репозитора

fun getChildDiscussions(parentId: Int): List<DiscussionEntity>? {
        return GetChildDiscussionAsyncTask(discussionDao).execute(parentId).get()
    }

private class GetChildDiscussionAsyncTask constructor(private val discussionDao: DiscussionDao?): AsyncTask<Int, Void, List<DiscussionEntity>?>() {
        override fun doInBackground(vararg params: Int?): List<DiscussionEntity>? {
            return discussionDao?.getChildDiscussionList(params[0]!!)
        }
    }

Класс Дао

@Query("SELECT * FROM discussion_table WHERE parent_id = :parentId")
fun getChildDiscussionList(parentId: Int): List<DiscussionEntity>?
person Naveen Kumar M    schedule 13.02.2019