问题
I have a Simple DAO including CRUD function
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);
}
For the select
, it is okay to return the LiveData type.
Inside the Activity the code is pretty for the selection
viewModel.getFeedEntrys().observe(this,entries -> {...});
However, when I try to insert, update , delete the data. The code seems a little bit ugly and also create a asynctask every time.
new AsyncTask<FeedEntry, Void, Void>() {
@Override
protected Void doInBackground(FeedEntry... feedEntries) {
viewModel.update(feedEntries[0]);
return null;
}
}.execute(feedEntry);
I have 2 question about it:
- Can I use LiveData to wrap the delete, insert , update function ?
- Better way to maintain such asynctask class for delete, insert , update?
Appreciate any suggestions and advices. Thank you.
回答1:
- Can i use LiveData to wrap Delete, Insert, Update calls?
No, you can't. I wrote an answer to the issue. The reason is, that LiveData is used to notify for changes. Insert, Update, Delete won't trigger a change. It will return the deleted rows, the inserted ID or the affected rows. Even if it looks horrible it makes sense not having LiveData wrapped around your stuff. Anyway, it would make sense to have something like Single around the calls to let the operation triggered and operated on a RX-Java operation.
If you want to trigger those calls, you observe on a selection query which notify your LiveData onec you have updated, inserted or deleted some/any data.
- Better way to maintain such asynctask class for delete, insert , update?
After looking at your example it looks like that you misuse the (Model/View/)ViewModel-Pattern. You should never access your repository in your view. I'm not sure if you'r doing this because its not visible in your sample. Anyway, after observing your LiveData and getting a result, there's no need to wrap the updating of data inside your viewModel in an AsyncTask. That means, that you should alway take care of
a) view <-> viewmodel <-> repository and not view <-> repository and view <-> viewmodel
and
b) don't try to use threads which are not needed. You observe LiveData on a Background Thread (@WorkerThread) by default (if not annotated with @MainThread) and get the value in the ui-thread (@MainThread).
回答2:
You can use @Dao annotation in abstract classes too, so:
- Create an abstract
@Dao BaseDao
class with the abstract methods@Insert insert(entities)
and with the concrete methodinsert(entities, callback)
that do that uglyAsyncTask
job, calling the abstract@Insert insert(entities)
ononBackground
and your callback ononPostExecute
. - Make your
FeedEntryDAO
also abstract extendBaseDao
and the@Query
methods abstract.
The result usage in Kotlin is quite pretty:
database.entityDao().insert(entities) { ids ->
// success
}
回答3:
For the second question, there is another neater alternative to AsyncTask; which is using java Executor, the good news is that you can use a single instance of Executor
instead of multiple instances of the AsyncTask
for all CRUD operations.
Demo Example
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:
Concerning question 2:
For Kotlin users there is now a really nice way to achieve this, because since Room 2.1 there is direct support for coroutines. A neat example is given here.
You can use a "suspend function" directly in the DAO, which takes care that nothing is executed on the main thread:
@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)
}
then in your viewModel you would just:
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)
}
回答5:
To app's UI to update automatically when the data changes this, use a return value of type LiveData in your query method description. Room generates all necessary code to update the LiveData when the database is updated.
@Dao
interface MyDao {
@Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
fun loadUsersFromRegionsSync(regions: List<String>): LiveData<List<User>>
}
Note: As of version 1.0, Room uses the list of tables accessed in the query to decide whether to update instances of LiveData.
来源:https://stackoverflow.com/questions/46462128/android-room-livedata-callback-of-update-insert