Номер Android: обратный вызов LiveData для вставки обновления?

У меня есть простой DAO, включая функцию CRUD

FeedEntryDAO.java

@Dao
public interface FeedEntryDAO {

  @Query("SELECT * FROM feedEntrys")
  LiveData<List<FeedEntry>> getAll();

  @Query("SELECT * FROM feedEntrys WHERE uid = :uid LIMIT 1")
  LiveData<FeedEntry> findByUid(int uid);

  @Insert
  void insertAll(FeedEntry... feedEntries);

  @Delete
  void delete(FeedEntry feedEntry);

  @Update
  int update(FeedEntry feedEntry);

}

Для select можно вернуть тип LiveData.

Внутри Activity код хорош для выбора

viewModel.getFeedEntrys().observe(this,entries -> {...});

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

new AsyncTask<FeedEntry, Void, Void>() {
                @Override
                protected Void doInBackground(FeedEntry... feedEntries) {
                  viewModel.update(feedEntries[0]);
                  return null;
                }
}.execute(feedEntry);

У меня есть 2 вопроса об этом:

  1. Могу ли я использовать LiveData для переноса функции удаления, вставки, обновления?
  2. Лучший способ поддерживать такой класс asynctask для удаления, вставки, обновления?

Ценю любые предложения и советы. Спасибо.

Ответ 1

  1. Могу ли я использовать LiveData для переноса вызовов Удалить, Вставить, Обновить?

Нет, ты не можешь. Я написал ответ на вопрос. Причина в том, что LiveData используется для уведомления об изменениях. Вставить, Обновить, Удалить не вызовет изменения. Он вернет удаленные строки, вставленный идентификатор или затронутые строки. Даже если это выглядит ужасно, имеет смысл не включать LiveData в ваши вещи. В любом случае, имеет смысл иметь что-то вроде Single в вызовах, чтобы операция запускалась и работала на операции RX-Java.

Если вы хотите инициировать эти вызовы, вы наблюдаете запрос выбора, который уведомляет ваш LiveData onec, что вы обновили, вставили или удалили некоторые/любые данные.

  1. Лучший способ поддерживать такой класс asynctask для удаления, вставки, обновления?

Посмотрев на ваш пример, похоже, что вы неправильно используете (Model/View/) ViewModel-Pattern. Вы никогда не должны получать доступ к вашему хранилищу, по вашему мнению. Я не уверен, что вы делаете это, потому что это не видно в вашем образце. В любом случае, после просмотра ваших LiveData и получения результата, нет необходимости оборачивать обновление данных внутри вашей viewModel в AsyncTask. Это означает, что вы должны всегда заботиться о

a) просматривать <-> viewmodel <-> хранилище и не просматривать <-> хранилище и просматривать <-> viewmodel

а также

б) не пытайтесь использовать темы, которые не нужны. Вы наблюдаете LiveData в фоновом потоке (@WorkerThread) по умолчанию (если не помечены @MainThread) и получаете значение в потоке пользовательского интерфейса (@MainThread).

Ответ 2

Вы также можете использовать аннотацию @Dao в абстрактных классах, поэтому:

  1. Создайте абстрактный класс @Dao BaseDao с помощью абстрактных методов @Insert insert(entities) и с конкретным методом insert(entities, callback) которые выполняют это уродливое задание AsyncTask, вызывая абстрактную @Insert insert(entities) в onBackground и обратный вызов в onPostExecute.
  2. Сделайте ваш FeedEntryDAO также абстрактным расширением BaseDao и @Query методами @Query.

Результат использования в Kotlin довольно симпатичен:

database.entityDao().insert(entities) { ids ->
    // success
}

Ответ 3

Что касается второго вопроса, есть еще более аккуратная альтернатива AsyncTask; которая использует Java Executor, хорошая новость заключается в том, что вы можете использовать один экземпляр Executor вместо нескольких экземпляров AsyncTask для всех операций CRUD.

Демо-пример

public class Repository {

    private static Repository instance;
    private MyDatabase mDatabase;
    private Executor mExecutor = Executors.newSingleThreadExecutor();

    private Repository(Application application) {
        mDatabase = MyDatabase.getInstance(application.getApplicationContext());
    }

    public static Repository getInstance(Application application) {
        if (instance == null) {
            instance = new Repository(application);
        }
        return instance;
    }


    public void insert(final MyModel model) {

        mExecutor.execute(new Runnable() {
            @Override
            public void run() {
                mDatabase.getMyModelDAO().insert(model);
            }
        });
    }

    public void update(final MyModel model) {
        mExecutor.execute(new Runnable() {
            @Override
            public void run() {
                mDatabase.getMyModelDAO().update(model);
            }
        });
    }

    public void delete(final MyModel model) {
        mExecutor.execute(new Runnable() {
            @Override
            public void run() {
                mDatabase.getMyModelDAO().delete(model);
            }
        });
    }

}

Ответ 4

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

@Dao
interface MyDao {
    @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
    fun loadUsersFromRegionsSync(regions: List<String>): LiveData<List<User>>
}

Примечание. Начиная с версии 1.0, Room использует список таблиц, к которым осуществляется доступ в запросе, чтобы решить, обновлять ли экземпляры LiveData.

Ответ 5

По вопросу 2:

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

Вы можете использовать "функцию приостановки" непосредственно в DAO, которая заботится о том, чтобы ничего не выполнялось в основном потоке:

@Dao
 interface BarDao {

   @Query("SELECT * FROM bar WHERE groupId = 2")
   fun getAllBars(): LiveData<MutableList<Bar>>

   @Query( "SELECT * FROM bar WHERE groupId = 0 LIMIT 1")
   fun getRecentBar(): LiveData<Bar>

   @Insert
   suspend fun insert(bar: Bar)

   @Update
   suspend fun update(bar: Bar)

   @Delete
   suspend fun delete(bar: Bar)

}

тогда в вашей viewModel вы просто:

    fun insert(bar: Bar) = viewModelScope.launch {
        barDao.insert(bar)
    }

    fun update(bar: Bar) = viewModelScope.launch {
        barDao.update(bar)
    }

    fun delete(bar: Bar)= viewModelScope.launch {
        barDao.delete(bar)
    }