Room 是一個持久化的庫,是 Android Architecture Components
的一部分。使用 Room 可以更容易的處理 app 中的 SQLiteDatabase 物件,減少模板程式碼的數量以及在編譯時驗證 SQL 查詢語句。
下面是在 app
中使用 Room 的七個基本步驟:
-
更新專案下的
build.gradle
依賴allprojects { repositories { google () jcenter () } } 複製程式碼
在同檔案(也可能是
versions.gradle
)下配置 roomVersionext { … roomVersion = '…' } 複製程式碼
在
app/build.gradle
中新增 Room 的依賴dependence { … implementation "android.arch.persistence.room:runtime:$rootProject.roomVersion" annotationProcessor "android.arch.persistence.room:compiler:$rootProject.roomVersion" androidTestimplementation "android.arch.persistence.room:testing:$rootProject.roomVersion" } 複製程式碼
對於從 SQLiteDatabaseHelper 遷移到 Room 的,需要實現一個 Migration 類來保持 user 資料需要升級資料庫的版本號。需要調整架構來測試這個遷移。為此需要在
app/build.gradle
新增如下程式碼:android { defaultConfig { ... //用於 Room 的遷移測試 javaCompileOptions { annotationProcessorOptions { arguments = ["room.schemaLocations": "$projectDir/schemas".toString()] } } } //用於 Room 的遷移測試 sourceSets { androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } } 複製程式碼
-
將 model 類更新為 entity
- 使用
@Entity
標註這個類,並將表名設定給tableName
屬性; - 給主鍵對應的欄位新增
@PrimaryKey
註解 - 給欄位設定
@ColumnInfo(name = "column_name")
註解 - 如果有多個建構函式可用,新增
@Ignore
註解告訴 Room 哪個要用,哪個不要。
@Entity(tableName = "Users") public class User { @PrimaryKey @ColumnInfo(name = "userid") private String mId; @ColumnInfo(name = "username") private String mUserName; @ColumnInfo(name = "last_update") private Date mDate; @Ignore public User(String userName) { mId = UUID.randomUUID().toString(); mUserName = userName; mDate = new Date(System.currentTimeMillis()); } public User(String id, String userName, Date date) { this.mId = id; this.mUserName = userName; this.mDate = date; } ... } 複製程式碼
- 使用
-
建立資料訪問物件
Data Access Objects
(DAOs)DAOs
負責定義訪問資料庫的方法。在專案的初始化 SQLite 實現中,所有的資料庫查詢都在LocalUserDataSource
中使用Cursor
物件完成。使用 Room,不需要關聯程式碼所有的Cursor
並且在UserDao
類中使用註解簡單定義了查詢操作。例如,需要查詢資料庫中的所有使用者時,我們只需要寫如下程式碼,Room 就會處理所有這些繁重的任務。
@Query("SELECT * FROM Users") List<User> getUsers(); 複製程式碼
-
建立資料庫
到目前為止,已經定義了
Users
表,和它相關的查詢操作,但還沒有建立將其他 Room 元件組合在一起的資料庫。為了實現這一點,我們需要定義一個繼承了RoomDatabase
的抽象類。這個類用@Database
註解修飾,列舉了包含在資料庫中的 entity 以及操作他們的 DAO。@Database(entities = {User.class}, version = 2) @TypeConverters(DataConverter.class) public abstract class UserDatabase extends RoomDatabase { private static UsersDatabase INSTANCE; public abstract UserDao userDao(); 複製程式碼
如果是從版本
1
遷移到2
,那麼需要實現一個Migration
類告訴 Room 遷移時需要做什麼。這裡得資料庫架構沒有發生變化,所以只需要一個空實現即可。static final Migration MIGRATION_1_2 = new Migration(1, 2) { @Override public void migration(SupportSQLiteDatabase database) { //資料庫無修改,空實現 } }; 複製程式碼
在
UsersDatabase
類中建立資料庫物件,定義資料庫的名稱和遷移database = Room.databaseBuilder(context.getApplicationContext(), UsersDatabse.class, "Sample.db") .addMigrations(MIGRATION_1_2) .build(); } 複製程式碼
更多關於資料庫遷移是如何實現的以及底層是如何執行得,請參見下文的文章:
-
在這一步,將修改
LocalUserDatabaseSource
類來使用UserDao
的方法。為了做到這一點,首先要通過移除Context
,新增UserDao
來修改建構函式。當然,其他例項化LocalUserDatabaseSource
的類也需要修改。然後,呼叫UserDao
的方法更新LocalDatabaseSource
的查詢資料庫的方法。public List<User> getUsers() { return mUserDao.getUsers(); } 複製程式碼
執行程式碼!Room 的一個非常棒的特性就是,如果你的資料庫操作在主執行緒執行,你的 app 會崩潰,會顯示下面的異常資訊:
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time. 複製程式碼
-
上機實測
現在測試建立的
UserDao
,UsersDatabase
和LocalUserDatabaseSource
測試UserDao
建立一個
AndroidJUnit4
測試類用於測試UserDao
。Room 的一個很棒的特性就是可以在記憶體中建立資料庫,避免需要清除每次的測試用例。@Before public void initDb() throws Exception { mDatabse = Room.inMemoryDatabaseBuilder( InstrumentationRegistry.getContext(), UsersDatabse.class) .build(); } 複製程式碼
確保每次測試後關閉了資料庫連線
@After public void closeDb() throws Exception { mDatabase.close(); } 複製程式碼
為了測試插入一個
User
,例如,可以先插入這個物件,再檢查能否從資料庫中得到這個物件。@Test public void insertAndGetUser() { // When inserting a new user in the data source mDatabase.userDao().insertUser(USER); //The user can be retrieved List<User> users = mDatabase.userDao().getUsers(); assertThat(users.size(), is(1)); User dbUser = users.get(0); assertEquals(dbUser.getId(), USER.getId()); assertEquals(dbUser.getUserName(), USER.getUserName()); } 複製程式碼
測試
LocalUserDataSource
中的UserDao
使用 我們需要做的是建立一個記憶體中的資料庫,並從中獲取一個UserDao
,並將其作為LocalUserDataSource
建構函式的引數。@Before public void initDb() throws Exception { mDatabase = Room.inMemoryDatabaseBuilder( InstrumentationRegistry.getContext(), UsersDatabase.class) .build(); mDataSource = new LocalUserDataSource(mDatabase.userDao()); } 複製程式碼
MigrationTsetHelper如何使用,請參見下面的部落格文章:
-
刪除
UserDbHelper
類