Android協(xié)程——Room&Coroutines

在Room2.1版本中提供了對(duì)協(xié)程的支持。Dao層的方法可以被suspend標(biāo)記來確保他們?cè)谥骶€程中被執(zhí)行。接下來,我們就來看看如何使用并為它寫一個(gè)簡單的單元測試。

Demo地址:https://github.com/jotyy/coroutines-retrofit-example 歡迎交流和star,謝謝

為你的數(shù)據(jù)庫加點(diǎn)suspending

首先我們要為項(xiàng)目加上Room的依賴,并確保版本在2.1及以上。

implementation "androidx.room:room-coroutines:${versions.room}"

同時(shí),我們的還需要Kotlin版本在1.3.0以上,以及Coroutines 1.0.0以上。

接下來我們可以編寫如下的DAO層,使用suspend標(biāo)記方法。

@Dao
interface UsersDao{
    @Query
    suspend fun getUsers(): List<User>
    
    @Query
    suspend fun incrementUserAge(userId: String)
    
    @Insert
    suspend fun insertUser(user: User)
    
    @Update
    suspend fun updateUser(user: User)
    
    @Delete
    suspend fun deleteUser(user: User)
}

被@Transacition注解的方法也可以使用suspend關(guān)鍵字標(biāo)記,并且可以調(diào)用其他被suspend標(biāo)記的DAO層方法。

@Dao
abstract class UsersDao{
    @Transaction
    open suspend fun setLoggedInUser(loggerInUser: User){
        deleteUser(loggedInUser)
        insertUser(loggedInUser)
    }
    
    @Query("DELETE FROM users")
    abstract fun deleteUser(user: User)

    @Insert
    abstract suspend fun insertUser(user: User)
}

同樣,你也可以從不同的DAO中調(diào)用suspend方法。

class Repository(val database: MyDatabase) {
    
    suspend fun clearData(){
        database.withTransaction {
            database.userDao().deleteLoggerInUser()
            database.commentsDao().deleteComments()
        }
    }
}

你可以在創(chuàng)建數(shù)據(jù)庫時(shí)通過調(diào)用setTransactionExecutor()方法或setQueryExecutor()方法來提供executors去控制它們運(yùn)行的線程。默認(rèn)情況下,這將是用于在后臺(tái)線程上運(yùn)行查詢的相同執(zhí)行程序。

編寫單元測試

測試DAO suspend方法和測試其它的suspend方法是一樣的。例如,要檢查插入用戶后我們能夠檢索它,我們將測試包裝在runBlocking塊中:

@Test
fun insertAndGetUser() = runBlocking {
    //提供一個(gè)插入到數(shù)據(jù)庫中的User
    userDao.insertUser(user)
    
    // 通過DAO獲取Users
    val usersFromDb = userDao.getUsers()
    
    //驗(yàn)證
    assertEquals(listOf(user), userFromDb)
}

再深入一點(diǎn)去看

進(jìn)一步,讓我們來看看為同步和暫停插入生成的DAO類實(shí)現(xiàn):

@Insert
fun insertUserSync(user: User)

@Insert
suspend fun insertUser(user: User)

在同步插入的方式中,生成的代碼啟動(dòng)事務(wù),執(zhí)行插入,將事務(wù)標(biāo)記為成功并結(jié)束它。同步方法只在任何調(diào)用它的線程上的執(zhí)行insert。

@Override 
public void insertUserSync(final User user) {
    _db.beginTransaction();
    try{
        _insertionAdapterOfUser.insert(user);
        _db.setTransactionSuccessful();
    } finally {
        _db.endTransaction();
    }
}

我們?cè)賮砜纯词褂胹uspend關(guān)鍵字的方法是如何處理的。

@Override
public Object insertUserSuspend(final User user,
                               final Continuation<? super Unit> p1){
    
    return CoroutinesRoom.execute(_db,new Callable<Unit>(){
        @Override
        public Unit call() throws Exception {
            _db.beginTransaction();
            try {
                _insertionAdapterOfUser.insert(user);
                _db.setTransactionSuccessful();
                return kotlin.Unit.INSTANCE;
            } finally {
                _db.endTransaction();
            }
        }
    }, p1);
}

生成的代碼中確保了插入不發(fā)生在UI線程上。在我們的suspend函數(shù)實(shí)現(xiàn)中,同步insert方法中的相同邏輯包含在Callable中。Room調(diào)用CoroutinesRoom.execute掛起函數(shù),該函數(shù)切換到后臺(tái)調(diào)度程序,具體取決于數(shù)據(jù)庫是否已打開且我們是否處于事務(wù)中。這是函數(shù)的實(shí)現(xiàn):

@JvmStatic
suspend fun <R> execute(
    db: RoomDatabase,
    inTransaction: Boolean,
    callable: Callable<R>
): R {
    if (db.isOpen && db.inTransaction) {
        return callable.call()
    }
    
    val context = coroutineContext[TransactionElement]?.transactionDispatcher
        ?: if (inTransaction) db.transactionDispatcher else db.queryDispatcher
    return withContext(context) {
        callable.call()
    }
}

情形一:數(shù)據(jù)庫打開,且我們?cè)谑聞?wù)中

此時(shí)我們直接執(zhí)行callable即可

情形二:其他

Room確保Callable中的工作已完成(call方法在后臺(tái)執(zhí)行)。

Room會(huì)使用不同的dispatcher來處理事務(wù)和查詢。這些是從構(gòu)建數(shù)據(jù)庫時(shí)提供的執(zhí)行程序派生的,或者默認(rèn)情況下將使用系統(tǒng)組件IO執(zhí)行程序,這和LiveData執(zhí)行后臺(tái)任務(wù)的executor是一樣的。

在應(yīng)用程序中開始使用Room和coroutines,保證數(shù)據(jù)庫工作在非UI Dispatcher上運(yùn)行。使用suspend修飾符標(biāo)記您的DAO方法,并從其他掛起函數(shù)或協(xié)程中調(diào)用它們!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容