最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Room Kotlin API的使用入門教程

 更新時(shí)間:2021年04月10日 11:13:32   作者:Android_開發(fā)者  
這篇文章主要介紹了Room Kotlin API使用入門教程,幫助大家更好的理解和學(xué)習(xí)使用并且測試 Room Kotlin API,感興趣的朋友可以了解下

Room 是 SQLite 的封裝,它使 Android 對數(shù)據(jù)庫的操作變得非常簡單,也是迄今為止我最喜歡的 Jetpack 庫。在本文中我會(huì)告訴大家如何使用并且測試 Room Kotlin API,同時(shí)在介紹過程中,我也會(huì)為大家分享其工作原理。

我們將基于 Room with a view codelab 為大家講解。這里我們會(huì)創(chuàng)建一個(gè)存儲(chǔ)在數(shù)據(jù)庫的詞匯表,然后將它們顯示到屏幕上,同時(shí)用戶還可以向列表中添加單詞。

定義數(shù)據(jù)庫表

在我們的數(shù)據(jù)庫中僅有一個(gè)表,就是保存詞匯的表。Word 類代表表中的一條記錄,并且它需要使用注解 @Entity。我們使用 @PrimaryKey 注解為表定義主鍵。然后,Room 會(huì)生成一個(gè) SQLite 表,表名和類名相同。每個(gè)類的成員對應(yīng)表中的列。列名和類型與類中每個(gè)字段的名稱和類型一致。如果您希望改變列名而不使用類中的變量名稱作為列名,可以通過 @ColumnInfo 注解來修改。

/* Copyright 2020 Google LLC.  
   SPDX-License-Identifier: Apache-2.0 */

@Entity(tableName = "word_table")
data class Word(@PrimaryKey @ColumnInfo(name = "word") val word: String)

我們推薦大家使用 @ColumnInfo 注解,因?yàn)樗梢允鼓`活地對成員進(jìn)行重命名而無需同時(shí)修改數(shù)據(jù)庫的列名。因?yàn)樾薷牧忻麜?huì)涉及到修改數(shù)據(jù)庫模式,因而您需要實(shí)現(xiàn)數(shù)據(jù)遷移。

訪問表中的數(shù)據(jù)

如需訪問表中的數(shù)據(jù),需要?jiǎng)?chuàng)建一個(gè)數(shù)據(jù)訪問對象 (DAO)。也就是一個(gè)叫做 WorkDao 的接口,它會(huì)帶有 @Dao 注解。我們希望通過它實(shí)現(xiàn)表級別的數(shù)據(jù)插入、刪除和獲取,所以數(shù)據(jù)訪問對象中會(huì)定義相應(yīng)的抽象方法。操作數(shù)據(jù)庫屬于比較耗時(shí)的 I/O 操作,所以需要在后臺線程中完成。我們將把 Room 與 Kotlin 協(xié)程和 Flow 相結(jié)合來實(shí)現(xiàn)上述功能。

/* Copyright 2020 Google LLC.  
   SPDX-License-Identifier: Apache-2.0 */

@Dao
interface WordDao {
    @Query("SELECT * FROM word_table ORDER BY word ASC")
    fun getAlphabetizedWords(): Flow<List<Word>>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insert(word: Word)
}

我們在視頻 Kotlin Vocabulary 中介紹了 協(xié)程的相關(guān)基本概念, 在 Kotlin Vocabulary 另一個(gè)視頻中則介紹了 Flow 相關(guān)的內(nèi)容

插入數(shù)據(jù)

要實(shí)現(xiàn)插入數(shù)據(jù)的操作,首先創(chuàng)建一個(gè)抽象的掛起函數(shù),需要插入的單詞作為它的參數(shù),并且添加 @Insert 注解。Room 會(huì)生成將數(shù)據(jù)插入數(shù)據(jù)庫的全部操作,并且由于我們將函數(shù)定義為可掛起,所以 Room 會(huì)將整個(gè)操作過程放在后臺線程中完成。因此,該掛起函數(shù)是主線程安全的,也就是在主線程可以放心調(diào)用而不必?fù)?dān)心阻塞主線程。

@Insert
suspend fun insert(word: Word)

在底層 Room 生成了 Dao 抽象函數(shù)的實(shí)現(xiàn)代碼。下面代碼片段就是我們的數(shù)據(jù)插入方法的具體實(shí)現(xiàn):

/* Copyright 2020 Google LLC.  
   SPDX-License-Identifier: Apache-2.0 */

@Override
public Object insert(final Word word, final Continuation<? super Unit> p1) {
    return CoroutinesRoom.execute(__db, true, new Callable<Unit>() {
      @Override
      public Unit call() throws Exception {
          __db.beginTransaction();
          try {
              __insertionAdapterOfWord.insert(word);
              __db.setTransactionSuccessful();
          return Unit.INSTANCE;
          } finally {
              __db.endTransaction();
          }
      }
    }, p1);
}

CoroutinesRoom.execute() 函數(shù)被調(diào)用,里面包含三個(gè)參數(shù): 數(shù)據(jù)庫、一個(gè)用于表示是否正處于事務(wù)中的標(biāo)識、一個(gè) Callable 對象。Callable.call() 包含處理數(shù)據(jù)庫插入數(shù)據(jù)操作的代碼。

如果我們看一下 CoroutinesRoom.execute() 的 實(shí)現(xiàn),我們會(huì)看到 Room 將 callable.call() 移動(dòng)到另外一個(gè) CoroutineContext。該對象來自構(gòu)建數(shù)據(jù)庫時(shí)您所提供的執(zhí)行器,或者默認(rèn)使用 Architecture Components IO Executor。

查詢數(shù)據(jù)

為了能夠查詢表數(shù)據(jù),我們這里創(chuàng)建一個(gè)抽象函數(shù),并且為其添加 @Query 注解,注解后緊跟 SQL 請求語句: 該語句從單詞數(shù)據(jù)表中請求全部單詞,并且以字母順序排序。

我們希望當(dāng)數(shù)據(jù)庫中的數(shù)據(jù)發(fā)生改變的時(shí)候,能夠得到相應(yīng)的通知,所以我們返回一個(gè) Flow<List<Word>>。由于返回類型是 Flow,Room 會(huì)在后臺線程中執(zhí)行數(shù)據(jù)請求。

@Query(“SELECT * FROM word_table ORDER BY word ASC”)
fun getAlphabetizedWords(): Flow<List<Word>>

在底層,Room 生成了 getAlphabetizedWords():

/* Copyright 2020 Google LLC.  
   SPDX-License-Identifier: Apache-2.0 */

@Override
public Flow<List<Word>> getAlphabetizedWords() {
  final String _sql = "SELECT * FROM word_table ORDER BY word ASC";
  final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
  return CoroutinesRoom.createFlow(__db, false, new String[]{"word_table"}, new Callable<List<Word>>() {
    @Override
    public List<Word> call() throws Exception {
      final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
      try {
        final int _cursorIndexOfWord = CursorUtil.getColumnIndexOrThrow(_cursor, "word");
        final List<Word> _result = new ArrayList<Word>(_cursor.getCount());
        while(_cursor.moveToNext()) {
        final Word _item;
        final String _tmpWord;
        _tmpWord = _cursor.getString(_cursorIndexOfWord);
        _item = new Word(_tmpWord);
        _result.add(_item);
        }
        return _result;
      } finally {
        _cursor.close();
      }
    }
    @Override
    protected void finalize() {
      _statement.release();
    }
  });
}

我們可以看到代碼里調(diào)用了 CoroutinesRoom.createFlow(),它包含四個(gè)參數(shù): 數(shù)據(jù)庫、一個(gè)用于標(biāo)識我們是否正處于事務(wù)中的變量、一個(gè)需要監(jiān)聽的數(shù)據(jù)庫表的列表 (在本例中列表里只有 word_table) 以及一個(gè) Callable 對象。Callable.call() 包含需要被觸發(fā)的查詢的實(shí)現(xiàn)代碼。

如果我們看一下 CoroutinesRoom.createFlow() 的 實(shí)現(xiàn)代碼,會(huì)發(fā)現(xiàn)這里同數(shù)據(jù)請求調(diào)用一樣使用了不同的 CoroutineContext。同數(shù)據(jù)插入調(diào)用一樣,這里的分發(fā)器來自構(gòu)建數(shù)據(jù)庫時(shí)您所提供的執(zhí)行器,或者來自默認(rèn)使用的 Architecture Components IO 執(zhí)行器。

創(chuàng)建數(shù)據(jù)庫

我們已經(jīng)定義了存儲(chǔ)在數(shù)據(jù)庫中的數(shù)據(jù)以及如何訪問他們,現(xiàn)在我們來定義數(shù)據(jù)庫。要?jiǎng)?chuàng)建數(shù)據(jù)庫,我們需要?jiǎng)?chuàng)建一個(gè)抽象類,它繼承自 RoomDatabase,并且添加 @Database 注解。將 Word 作為需要存儲(chǔ)的實(shí)體元素傳入,數(shù)值 1 作為數(shù)據(jù)庫版本。

我們還會(huì)定義一個(gè)抽象方法,該方法返回一個(gè) WordDao 對象。所有這些都是抽象類型的,因?yàn)?Room 會(huì)幫我們生成所有的實(shí)現(xiàn)代碼。就像這里,有很多邏輯代碼無需我們親自實(shí)現(xiàn)。

最后一步就是構(gòu)建數(shù)據(jù)庫。我們希望能夠確保不會(huì)有多個(gè)同時(shí)打開的數(shù)據(jù)庫實(shí)例,而且還需要應(yīng)用的上下文來初始化數(shù)據(jù)庫。一種實(shí)現(xiàn)方法是在類中添加伴生對象,并且在其中定義一個(gè) RoomDatabase 實(shí)例,然后在類中添加 getDatabase 函數(shù)來構(gòu)建數(shù)據(jù)庫。如果我們希望 Room 查詢不是在 Room 自身創(chuàng)建的 IO Executor 中執(zhí)行,而是在另外的 Executor 中執(zhí)行,我們需要通過調(diào)用 setQueryExecutor() 將新的 Executor 傳入 builder。

/* Copyright 2020 Google LLC.  
   SPDX-License-Identifier: Apache-2.0 */

companion object {
  @Volatile
  private var INSTANCE: WordRoomDatabase? = null
  fun getDatabase(context: Context): WordRoomDatabase {
    return INSTANCE ?: synchronized(this) {
      val instance = Room.databaseBuilder(
        context.applicationContext,
        WordRoomDatabase::class.java,
        "word_database"
        ).build()
      INSTANCE = instance
      // 返回實(shí)例
      instance
    }
  }
}

測試 Dao

為了測試 Dao,我們需要實(shí)現(xiàn) AndroidJUnit 測試來讓 Room 在設(shè)備上創(chuàng)建 SQLite 數(shù)據(jù)庫。

當(dāng)實(shí)現(xiàn) Dao 測試的時(shí)候,在每個(gè)測試運(yùn)行之前,我們創(chuàng)建數(shù)據(jù)庫。當(dāng)每個(gè)測試運(yùn)行后,我們關(guān)閉數(shù)據(jù)庫。由于我們并不需要在設(shè)備上存儲(chǔ)數(shù)據(jù),當(dāng)創(chuàng)建數(shù)據(jù)庫的時(shí)候,我們可以使用內(nèi)存數(shù)據(jù)庫。也因?yàn)檫@僅僅是個(gè)測試,我們可以在主線程中運(yùn)行請求。

/* Copyright 2020 Google LLC.  
   SPDX-License-Identifier: Apache-2.0 */

@RunWith(AndroidJUnit4::class)
class WordDaoTest {
  
  private lateinit var wordDao: WordDao
  private lateinit var db: WordRoomDatabase

  @Before
  fun createDb() {
      val context: Context = ApplicationProvider.getApplicationContext()
      // 由于當(dāng)進(jìn)程結(jié)束的時(shí)候會(huì)清除這里的數(shù)據(jù),所以使用內(nèi)存數(shù)據(jù)庫
      db = Room.inMemoryDatabaseBuilder(context, WordRoomDatabase::class.java)
          // 可以在主線程中發(fā)起請求,僅用于測試。
          .allowMainThreadQueries()
          .build()
      wordDao = db.wordDao()
  }

  @After
  @Throws(IOException::class)
  fun closeDb() {
      db.close()
  }
...
}

要測試單詞是否能夠被正確添加到數(shù)據(jù)庫,我們會(huì)創(chuàng)建一個(gè) Word 實(shí)例,然后插入數(shù)據(jù)庫,然后按照字母順序找到單詞列表中的第一個(gè),然后確保它和我們創(chuàng)建的單詞是一致的。由于我們調(diào)用的是掛起函數(shù),所以我們會(huì)在 runBlocking 代碼塊中運(yùn)行測試。因?yàn)檫@里僅僅是測試,所以我們無需關(guān)心測試過程是否會(huì)阻塞測試線程。

/* Copyright 2020 Google LLC.  
   SPDX-License-Identifier: Apache-2.0 */

@Test
@Throws(Exception::class)
fun insertAndGetWord() = runBlocking {
    val word = Word("word")
    wordDao.insert(word)
    val allWords = wordDao.getAlphabetizedWords().first()
    assertEquals(allWords[0].word, word.word)
}

除了本文所介紹的功能,Room 提供了非常多的功能性和靈活性,遠(yuǎn)遠(yuǎn)超出本文所涵蓋的范圍。比如您可以指定 Room 如何處理數(shù)據(jù)庫沖突、可以通過創(chuàng)建 TypeConverters 存儲(chǔ)原生 SQLite 無法存儲(chǔ)的數(shù)據(jù)類型 (比如 Date 類型)、可以使用 JOIN 以及其它 SQL 功能實(shí)現(xiàn)復(fù)雜的查詢、創(chuàng)建數(shù)據(jù)庫視圖、預(yù)填充數(shù)據(jù)庫以及當(dāng)數(shù)據(jù)庫被創(chuàng)建或打開的時(shí)候觸發(fā)特定動(dòng)作。

更多相關(guān)信息請查閱我們的 Room 官方文檔,如果想通過實(shí)踐學(xué)習(xí),可以訪問 Room with a view codelab。

以上就是Room Kotlin API使用入門教程的詳細(xì)內(nèi)容,更多關(guān)于Room Kotlin API使用的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論

金坛市| 呼伦贝尔市| 株洲县| 板桥市| 南康市| 剑河县| 青龙| 安新县| 乌审旗| 平遥县| 旺苍县| 高雄县| 宝山区| 黄石市| 科尔| 大英县| 雅江县| 安义县| 阿克陶县| 南木林县| 射洪县| 尚义县| 宁蒗| 互助| 辽阳市| 唐山市| 花莲市| 墨江| 麻栗坡县| 普洱| 三都| 陆川县| 南开区| 九龙坡区| 犍为县| 龙门县| 浏阳市| 卓资县| 襄樊市| 长白| 霍林郭勒市|