Using Room with LiveData and other third-party libraries
Note: This article is part of the advanced Room series which covers all the details about the Room persistence library. You can read all the articles here:
- Introduction to Room Persistent Library in Android
- Data Access Objects - DAO in Room
- Entity Relationship in Room
- How does Room work internally?
- Room Database Migrations
- Using Room with LiveData and other third-party libraries [You are here]
In this blog, we will learn how to use Room with LiveData and other third-party libraries.
Room with LiveData
In the previous articles, we have discussed how we can use Room persistence library in our Android applications and how Room internally works. Also, we discussed about the different kind of operations we can do easily with the help of Room. Using LiveData with Room is also one of them.
Room supports the integration of LiveData very easily. You just need to return LiveData from your DAO methods and Room takes care of everything else for you.
Observable queries with LiveData
When performing queries, you’ll often want your app’s UI to update automatically when the data changes. To achieve this, use a return value of type
in your query method description.
LiveData
@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun getAllLiveData(): LiveData<List<User>>
}
Room generates all necessary code to update the
when the database is updated. The generated code looks like this:
LiveData
@Override
public LiveData<List<User>> getAllLiveData() {
final String _sql = "SELECT * FROM users";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
return __db.getInvalidationTracker().createLiveData(new String[]{"users"}, false, new Callable<List<User>>() {
@Override
public List<User> call() throws Exception {
final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
final int _cursorIndexOfFirstName = CursorUtil.getColumnIndexOrThrow(_cursor, "first_name");
final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "last_name");
final List<User> _result = new ArrayList<User>(_cursor.getCount());
while(_cursor.moveToNext()) {
final User _item;
final int _tmpUid;
_tmpUid = _cursor.getInt(_cursorIndexOfUid);
final String _tmpFirstName;
_tmpFirstName = _cursor.getString(_cursorIndexOfFirstName);
final String _tmpLastName;
_tmpLastName = _cursor.getString(_cursorIndexOfLastName);
_item = new User(_tmpUid,_tmpFirstName,_tmpLastName);
_result.add(_item);
}
return _result;
} finally {
_cursor.close();
}
}
@Override
protected void finalize() {
_statement.release();
}
});
}
In the above code,
takes
__db
.getInvalidationTracker().createLiveData()
tableNames
array
,
boolean flag and
inTransaction
callable.
computeFunction
-
tableNames
RoomTrackingLiveData
-
inTransaction
-
computeFunction
We can also see that the
method of
call()
does the actual execution of our query. It’s invoked when an observer starts observing the LiveData or whenever any changes happen in observed tables.
computeFunction
Room with RxJava
In the previous articles, we have discussed about using Room persistence library in our Android applications. We also discussed about the various operations we can perform using Room DAOs. Working with RxJava observables also becomes very easy using Room.
For returning RxJava observables from your DAO methods, we need to add the following dependency in our
first:
build.gradle
dependencies {
def room_version = "2.2.5"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
// RxJava support for Room
implementation "androidx.room:room-rxjava2:$room_version"
}
Reactive queries with RxJava
Room provides the following support for return values of RxJava2 types:
-
@Query
Publisher
Flowable
Observable
-
@Insert
@Update
@Delete
Completable
Single<T>
Maybe<T>
@Dao
interface UserDao {
@Query("SELECT * from users where uid = :id LIMIT 1")
fun loadUserById(id: Int): Flowable<User>
@Insert
fun insertUsers(vararg users: User): Completable
@Delete
fun deleteAllUsers(users: List<User>): Single<Int>
}
Implementation of these methods look like the following:
@Override
public Completable insertLargeNumberOfUsers(final User... users) {
return Completable.fromCallable(new Callable<Void>() {
@Override
public Void call() throws Exception {
__db.beginTransaction();
try {
__insertionAdapterOfUser.insert(users);
__db.setTransactionSuccessful();
return null;
} finally {
__db.endTransaction();
}
}
});
}@Override
public Single<Integer> deleteAllUsers(final List<User> users) {
return Single.fromCallable(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int _total = 0;
__db.beginTransaction();
try {
_total +=__deletionAdapterOfUser.handleMultiple(users);
__db.setTransactionSuccessful();
return _total;
} finally {
__db.endTransaction();
}
}
});
}@Override
public Flowable<User> loadUserById(final int id) {
final String _sql = "SELECT * from users where uid = ? LIMIT 1";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
int _argIndex = 1;
_statement.bindLong(_argIndex, id);
return RxRoom.createFlowable(__db, false, new String[]{"users"}, new Callable<User>() {
@Override
public User call() throws Exception {
//Implementation
}
@Override
protected void finalize() {
_statement.release();
}
});
}
From the autogenerated code, we can see that Room is using the
operator to create
fromCallable()
and
Completable
. In
Single
, Room uses
RxRoom.
createFlowable
to create
Flowable.create()
.
Flowable
Code inside
method of callable is similar to the non-reactive implementation.
call()
Room with Kotlin Coroutines
In the previous articles, we have discussed about the Room persistence library and the advantages of using Room. One of the advantages of using Room is how easily we can integrate it with other libraries like Kotlin Coroutines. So let’s discuss how can we achieve it with the help of Room.
First of all, we need to add the following dependency for Kotlin coroutines support:
dependencies {
def room_version = "2.2.5"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version" // Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:$room_version"
}
Write async methods with Kotlin coroutines
You can add the
Kotlin keyword to your DAO methods to make them asynchronous using Kotlin coroutines functionality. This ensures that they cannot be executed on the main thread.
suspend
@Dao
interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUsers(vararg users: User) @Update
suspend fun updateUsers(vararg users: User) @Delete
suspend fun deleteUsers(vararg users: User) @Query("SELECT * FROM users")
suspend fun loadAllUsers(): Array<User>
}
Room generates the implementation of all the above methods. Implementation of
functions is very similar to non-suspend ones.
suspend
@Override
public Object insertUsers(final User[] users, final Continuation<? super Unit> p1) {
return CoroutinesRoom.execute(__db, true, new Callable<Unit>() {
@Override
public Unit call() throws Exception {
__db.beginTransaction();
try {
__insertionAdapterOfUser.insert(users);
__db.setTransactionSuccessful();
return Unit.INSTANCE;
} finally {
__db.endTransaction();
}
}
}, p1);
}
Here,
executes the passed callable asynchronously. Implementation of
CoroutinesRoom.
execute
is not available.
CoroutinesRoom.
execute
Non suspend function looks like the following:
@Override
public void insertUsers(final User... users) {
__db.assertNotSuspendingTransaction();
__db.beginTransaction();
try {
__insertionAdapterOfUser.insert(users);
__db.setTransactionSuccessful();
} finally {
__db.endTransaction();
}
}
The only difference between both implementations is that in case of
functions,
suspend
is responsible for suspending the execution flow.
CoroutinesRoom.
execute
This is all about the use of LiveData, RxJava, and Kotlin Coroutines with Room. Hope you enjoyed this blog.
You can also connect with me on LinkedIn , Twitter , Facebook and Github .
Thank You!!!