Android Room:更新插入的LiveData回调?

时间:2017-09-28 06:12:55

标签: android android-room android-architecture-components android-livedata

我有Simple 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 -> {...});

但是,当我尝试插入,更新,删除数据时。代码看起来有点难看,每次都会创建一个asynctask。

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

我有2个问题:

  1. 我可以使用LiveData来包装删除,插入,更新功能吗?
  2. 更好的方法来维护这样的asynctask类用于删除,插入,更新?
  3. 感谢任何建议和建议。谢谢。

7 个答案:

答案 0 :(得分:5)

  
      
  1. 我可以使用LiveData来包装删除,插入,更新通话吗?
  2.   

不,你不能。我写了issue的答案。原因是,LiveData用于通知更改。插入,更新,删除不会触发更改。它将返回已删除的行,插入的ID或受影响的行。即使它看起来很糟糕也没有意义,没有LiveData包裹你的东西。无论如何,在调用之间使用Single这样的东西是有意义的,让操作在RX-Java操作上触发和操作。

如果您想触发这些调用,您可以在选择查询中观察,该查询会通知您已更新,插入或删除某些/任何数据的LiveData。

  
      
  1. 更好的方法来维护这样的asynctask类用于删除,插入,更新?
  2.   

在查看您的示例后,您似乎滥用了(模型/视图/)ViewModel-Pattern。您永远不应该在视图中访问您的存储库。我不确定你是否这样做,因为它在你的样本中不可见。无论如何,在观察LiveData并获得结果之后,无需在viewModel中将数据更新包装在AsyncTask中。这意味着,你应该总是照顾

a)查看&lt; - &gt; viewmodel&lt; - &gt;存储库而不是视图&lt; - &gt;存储库和视图&lt; - &gt;视图模型

b)不要尝试使用不需要的线程。您默认情况下在后台线程(@WorkerThread)上观察LiveData(如果没有使用@MainThread注释)并在ui-thread中获取值(@MainThread)。

答案 1 :(得分:3)

要解决您的第二个问题:

Google发布了一个Android Room Codelab here,其中列出了用于在Android中实现Room的简洁MVVM体系结构:

MVVM Architecture

此处建议由Database类中的公共静态ExecutorService处理数据库操作。 ExecutorService类的位置可能有所不同,只要记住这个想法是在MVVM中,您的视图就不关心实际CURD处理数据的方式,这是ViewModel而不是View 的责任。

github repository for this code lab

简而言之,要将类似的想法应用到您的代码中,就像这样:

class YourRepository {
    private FeedEntryDAO mFeedEntryDAO;

    YourRepository(Application application) {
        YourDatabase db = YourDatabase.getDatabase(application);
        mFeedEntryDAO = db.feedEntryDAO();
        mAllWords = mWordDao.getAlphabetizedWords();
    }


    void update(FeedEntry feedEntry) {
        Database.databaseExecutor.execute(() - > {
            mFeedEntryDAO.update(feedEntry);
        });
    }
}

class YourViewModel extends ViewModel {
    private YourRepository mRepository;
    void update(FeedEntry feedEntry) {
        mRepository.update(feedEntry)
    }
}

这样做,您的View可以直接调用viewModel.update(feedEntries [0])。

要提及的一件事是 mFeedEntryDAO.update(feedEntry)将自动触发getFeedEntrys LiveData上的观察者的onChanged回调

这对您来说很方便。您可以了解有关触发如何发生的更多信息here

答案 2 :(得分:1)

对于第二个问题,AsyncTask还有另一个更巧妙的选择;使用Java Executor的人来说,好消息是您可以对所有CRUD操作使用Executor的单个实例,而不是AsyncTask的多个实例。

演示示例

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);
            }
        });
    }

}

答案 3 :(得分:1)

关于问题2:

对于Kotlin用户,现在有一种非常好的方法来实现这一目标, 因为自2.1室以来,直接支持协程。给出了一个整洁的示例here

您可以直接在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)
    }

答案 4 :(得分:1)

列出了每个受支持库的受支持返回类型here。为方便起见,我将表格包含在此处。

<头>
查询类型 Kotlin 语言特性 RxJava 番石榴 Jetpack 生命周期
一次性写入 协程 (orgUnitPath: getOrgUnitPath(accountType, formObject.accountType), ) suspendSingle<T>Maybe<T> Completable 不适用
一次性阅读 协程 (ListenableFuture<T>) suspend, Single<T> Maybe<T> 不适用
可观察读取 ListenableFuture<T> Flow<T>Flowable<T>Publisher<T> 不适用 Observable<T>

答案 5 :(得分:0)

您也可以在抽象类中使用@Dao批注,因此:

  1. 使用抽象方法@Dao BaseDao和具体方法@Insert insert(entities)创建一个抽象insert(entities, callback)类,该类执行难看的AsyncTask工作,调用抽象@Insert insert(entities)onBackground上,您的回调在onPostExecute上。
  2. 使您的FeedEntryDAO也抽象化扩展BaseDao@Query方法抽象化。

Kotlin中的结果用法很漂亮:

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

答案 6 :(得分:0)

要使应用程序的UI在数据更改时自动更新,请在查询方法说明中使用LiveData类型的返回值。更新数据库后,Room会生成所有必需的代码来更新LiveData。

pageant

注意:从1.0版开始,Room使用在 查询以决定是否更新LiveData实例。