Android Room:更新插入的 LiveData 回调?
Android Room : LiveData callback of update insert?
我有一个 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
,可以return 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 来包装删除、插入、更新功能吗?
- 为删除、插入、更新维护此类异步任务 class 的更好方法?
感谢任何建议和意见。谢谢。
- Can i use LiveData to wrap Delete, Insert, Update calls?
不,你不能。我写了一个答案给issue。原因是,LiveData 用于通知更改。插入、更新、删除不会触发更改。它将 return 删除的行、插入的 ID 或受影响的行。即使它看起来很糟糕,不让 LiveData 包裹在你的东西上也是有意义的。无论如何,在调用周围设置类似 Single 的东西以让操作在 RX-Java 操作上触发和操作是有意义的。
如果你想触发这些调用,你会观察到一个选择查询,它通知你的 LiveData onec 你已经更新、插入或删除了 some/any 数据。
- Better way to maintain such asynctask class for delete, insert , update?
查看您的示例后,您似乎误用了 (Model/View/)ViewModel-Pattern。您永远不应该在您的视图中访问您的存储库。我不确定您是否正在这样做,因为它在您的示例中不可见。无论如何,在观察您的 LiveData 并获得结果后,无需将数据更新包装在 AsyncTask 中的 viewModel 中。这意味着,您应该始终照顾
a) view <-> viewmodel <-> repository 而不是 view <-> repository and view <-> viewmodel
和
b) 不要尝试使用不需要的线程。默认情况下(如果未使用@MainThread 注释),您在后台线程 (@WorkerThread) 上观察 LiveData,并在 ui-thread (@MainThread).
中获取值
你也可以在摘要中使用@Dao注解classes,所以:
- 使用抽象方法
@Insert insert(entities)
和具体方法 insert(entities, callback)
创建一个抽象 @Dao BaseDao
class 来完成丑陋的 AsyncTask
工作,调用onBackground
上的摘要 @Insert insert(entities)
和 onPostExecute
上的回调。
- 让你的
FeedEntryDAO
也抽象扩展 BaseDao
和 @Query
方法抽象。
Kotlin 中的结果用法非常漂亮:
database.entityDao().insert(entities) { ids ->
// success
}
要让应用程序 UI 在数据更改时自动更新,请在查询方法描述中使用 LiveData 类型的 return 值。当数据库更新时,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 实例的查询。
对于第二个问题,还有一个比 AsyncTask; which is using 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);
}
});
}
}
关于问题2:
对于 Kotlin 用户来说,现在有一个非常好的方法来实现这一点,
因为从 Room 2.1 开始就直接支持协程。给出了一个简洁的例子 here.
您可以直接在 DAO 中使用 "suspend function",这样可以确保在主线程上不执行任何操作:
@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)
}
解决您的第二个问题:
Google 发布了一个 Android Room Codelab here,它为在 Android:
中实现 Room 制定了简洁的 MVVM 架构
这里的建议是让数据库操作由数据库 class 中的 public 静态 ExecutorService 处理。 ExecutorService class 的位置可能会有所不同,请记住这个想法是 在 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)
}
}
通过这样做,您的视图可以直接调用 viewModel.update(feedEntries[0]).
需要提及的一件重要事情是mFeedEntryDAO.update(feedEntry) 将自动触发您的观察者在 getFeedEntrys LiveData 上的 onChanged 回调。
这对您来说非常方便。您可以阅读有关触发器如何发生的更多信息 here.
列出了每个受支持库的受支持 return 类型 here。为了方便起见,我在此处包含了 table。
Query type
Kotlin language features
RxJava
Guava
Jetpack Lifecycle
One-shot write
Coroutines (suspend
)
Single<T>
, Maybe<T>
, Completable
ListenableFuture<T>
N/A
One-shot read
Coroutines (suspend
)
Single<T>
, Maybe<T>
ListenableFuture<T>
N/A
Observable read
Flow<T>
Flowable<T>
, Publisher<T>
, Observable<T>
N/A
LiveData<T>
我有一个 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
,可以return 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 来包装删除、插入、更新功能吗?
- 为删除、插入、更新维护此类异步任务 class 的更好方法?
感谢任何建议和意见。谢谢。
- Can i use LiveData to wrap Delete, Insert, Update calls?
不,你不能。我写了一个答案给issue。原因是,LiveData 用于通知更改。插入、更新、删除不会触发更改。它将 return 删除的行、插入的 ID 或受影响的行。即使它看起来很糟糕,不让 LiveData 包裹在你的东西上也是有意义的。无论如何,在调用周围设置类似 Single 的东西以让操作在 RX-Java 操作上触发和操作是有意义的。
如果你想触发这些调用,你会观察到一个选择查询,它通知你的 LiveData onec 你已经更新、插入或删除了 some/any 数据。
- Better way to maintain such asynctask class for delete, insert , update?
查看您的示例后,您似乎误用了 (Model/View/)ViewModel-Pattern。您永远不应该在您的视图中访问您的存储库。我不确定您是否正在这样做,因为它在您的示例中不可见。无论如何,在观察您的 LiveData 并获得结果后,无需将数据更新包装在 AsyncTask 中的 viewModel 中。这意味着,您应该始终照顾
a) view <-> viewmodel <-> repository 而不是 view <-> repository and view <-> viewmodel
和
b) 不要尝试使用不需要的线程。默认情况下(如果未使用@MainThread 注释),您在后台线程 (@WorkerThread) 上观察 LiveData,并在 ui-thread (@MainThread).
中获取值你也可以在摘要中使用@Dao注解classes,所以:
- 使用抽象方法
@Insert insert(entities)
和具体方法insert(entities, callback)
创建一个抽象@Dao BaseDao
class 来完成丑陋的AsyncTask
工作,调用onBackground
上的摘要@Insert insert(entities)
和onPostExecute
上的回调。 - 让你的
FeedEntryDAO
也抽象扩展BaseDao
和@Query
方法抽象。
Kotlin 中的结果用法非常漂亮:
database.entityDao().insert(entities) { ids ->
// success
}
要让应用程序 UI 在数据更改时自动更新,请在查询方法描述中使用 LiveData 类型的 return 值。当数据库更新时,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 实例的查询。
对于第二个问题,还有一个比 AsyncTask; which is using 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);
}
});
}
}
关于问题2:
对于 Kotlin 用户来说,现在有一个非常好的方法来实现这一点, 因为从 Room 2.1 开始就直接支持协程。给出了一个简洁的例子 here.
您可以直接在 DAO 中使用 "suspend function",这样可以确保在主线程上不执行任何操作:
@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)
}
解决您的第二个问题:
Google 发布了一个 Android Room Codelab here,它为在 Android:
中实现 Room 制定了简洁的 MVVM 架构这里的建议是让数据库操作由数据库 class 中的 public 静态 ExecutorService 处理。 ExecutorService class 的位置可能会有所不同,请记住这个想法是 在 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)
}
}
通过这样做,您的视图可以直接调用 viewModel.update(feedEntries[0]).
需要提及的一件重要事情是mFeedEntryDAO.update(feedEntry) 将自动触发您的观察者在 getFeedEntrys LiveData 上的 onChanged 回调。
这对您来说非常方便。您可以阅读有关触发器如何发生的更多信息 here.
列出了每个受支持库的受支持 return 类型 here。为了方便起见,我在此处包含了 table。
Query type | Kotlin language features | RxJava | Guava | Jetpack Lifecycle |
---|---|---|---|---|
One-shot write | Coroutines (suspend ) |
Single<T> , Maybe<T> , Completable |
ListenableFuture<T> |
N/A |
One-shot read | Coroutines (suspend ) |
Single<T> , Maybe<T> |
ListenableFuture<T> |
N/A |
Observable read | Flow<T> |
Flowable<T> , Publisher<T> , Observable<T> |
N/A | LiveData<T> |