28

我有一个简单的 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);

我有两个问题:

  1. 我可以使用 LiveData 来包装删除、插入、更新功能吗?
  2. 为删除、插入、更新维护这样的异步任务类的更好方法?

感谢任何建议和意见。谢谢你。

4

7 回答 7

7
  1. 我可以使用 LiveData 来包装 Delete、Insert、Update 调用吗?

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

如果你想触发这些调用,你会在一个选择查询上观察它,它会通知你的 LiveData onec 你已经更新、插入或删除了一些/任何数据。

  1. 为删除、插入、更新维护这样的异步任务类的更好方法?

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

a) 查看 <-> viewmodel <-> 存储库,而不是查看 <-> 存储库和查看 <-> viewmodel

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

于 2017-09-29T22:14:31.900 回答
4

对于第二个问题,AsyncTask有另一个更简洁的替代方案;这是使用 java Executor,好消息是您可以使用单个实例Executor而不是所有 CRUD 操作的多个实例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);
            }
        });
    }

}
于 2019-04-14T03:46:10.547 回答
4

关于问题 2:

对于 Kotlin 用户来说,现在有一种非常好的方法来实现这一点,因为从 Room 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)
    }
于 2019-10-08T17:00:28.223 回答
3

要解决您的第二个问题:

Google 在这里发布了一个 Android Room Codelab ,它为在 Android 中实现 Room 制定了一个简洁的 MVVM 架构:

MVVM 架构

这里的建议是让数据库类中的公共静态ExecutorService处理数据库操作。ExecutorService 类的位置可能会有所不同,只要记住这个想法是在 MVVM 中,您的视图并不关心数据实际上是如何 CURD 的,这是 ViewModel 的责任,而不是 View 的责任

此代码实验室的 github 存储库

简而言之,要将类似的想法应用于您的代码,应该是这样的:

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

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

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

在您的情况下,这非常方便。您可以在此处阅读有关触发器如何发生的更多信息。

于 2020-05-29T05:04:10.180 回答
3

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

查询类型 Kotlin 语言特性 RxJava 番石榴 喷气背包生命周期
一键写入 协程 ( suspend) Single<T>, Maybe<T>,Completable ListenableFuture<T> 不适用
一键阅读 协程 ( suspend) Single<T>,Maybe<T> ListenableFuture<T> 不适用
可观察读 Flow<T> Flowable<T>, Publisher<T>,Observable<T> 不适用 LiveData<T>

网络存档链接

于 2021-01-15T05:32:05.220 回答
1

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

  1. 使用抽象方法和具体方法创建一个抽象@Dao BaseDao类,这些方法可以完成这项丑陋的工作,调用抽象on和你的回调 on 。@Insert insert(entities)insert(entities, callback)AsyncTask@Insert insert(entities)onBackgroundonPostExecute
  2. 使您的FeedEntryDAO抽象扩展BaseDao@Query方法抽象。

Kotlin 中的结果用法非常漂亮:

database.entityDao().insert(entities) { ids ->
    // success
}
于 2019-02-15T13:51:57.747 回答
0

要在数据更改时自动更新应用程序的 UI,请在查询方法描述中使用 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 的实例。

于 2019-04-06T13:35:13.593 回答