Номер 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 вопроса об этом:
- Могу ли я использовать LiveData для переноса функции удаления, вставки, обновления?
- Лучший способ поддерживать такой класс asynctask для удаления, вставки, обновления?
Ценю любые предложения и советы. Спасибо.
Ответы
Ответ 1
- Могу ли я использовать LiveData для переноса вызовов Удалить, Вставить, Обновить?
Нет, ты не можешь. Я написал ответ на вопрос. Причина в том, что LiveData используется для уведомления об изменениях. Вставить, Обновить, Удалить не вызовет изменения. Он вернет удаленные строки, вставленный идентификатор или затронутые строки. Даже если это выглядит ужасно, имеет смысл не включать LiveData в ваши вещи. В любом случае, имеет смысл иметь что-то вроде Single в вызовах, чтобы операция запускалась и работала на операции RX-Java.
Если вы хотите инициировать эти вызовы, вы наблюдаете запрос выбора, который уведомляет ваш LiveData onec, что вы обновили, вставили или удалили некоторые/любые данные.
- Лучший способ поддерживать такой класс asynctask для удаления, вставки, обновления?
Посмотрев на ваш пример, похоже, что вы неправильно используете (Model/View/) ViewModel-Pattern. Вы никогда не должны получать доступ к вашему хранилищу, по вашему мнению. Я не уверен, что вы делаете это, потому что это не видно в вашем образце. В любом случае, после просмотра ваших LiveData и получения результата, нет необходимости оборачивать обновление данных внутри вашей viewModel в AsyncTask. Это означает, что вы должны всегда заботиться о
a) просматривать <-> viewmodel <-> хранилище и не просматривать <-> хранилище и просматривать <-> viewmodel
а также
б) не пытайтесь использовать темы, которые не нужны. Вы наблюдаете LiveData в фоновом потоке (@WorkerThread) по умолчанию (если не помечены @MainThread) и получаете значение в потоке пользовательского интерфейса (@MainThread).
Ответ 2
Вы также можете использовать аннотацию @Dao в абстрактных классах, поэтому:
- Создайте абстрактный класс
@Dao BaseDao
с помощью абстрактных методов @Insert insert(entities)
и с конкретным методом insert(entities, callback)
которые выполняют это уродливое задание AsyncTask
, вызывая абстрактную @Insert insert(entities)
в onBackground
и обратный вызов в onPostExecute
. - Сделайте ваш
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)
}