【譯】遷移到Room的7個步驟

NARUTONBM發表於2019-03-29

譯自7 Steps To Room

Room 是一個持久化的庫,是 Android Architecture Components 的一部分。使用 Room 可以更容易的處理 app 中的 SQLiteDatabase 物件,減少模板程式碼的數量以及在編譯時驗證 SQL 查詢語句。

下面是在 app 中使用 Room 的七個基本步驟:

  1. 更新專案下的 build.gradle 依賴

    allprojects {
    	repositories {
    		google ()
    		jcenter ()
    	}
    }
    複製程式碼

    在同檔案(也可能是 versions.gradle)下配置 roomVersion

    ext {
    	…
    	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())
        }
    }
    複製程式碼
  2. 將 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;
        }
        ...
    }
    複製程式碼
  3. 建立資料訪問物件Data Access Objects(DAOs)

    DAOs負責定義訪問資料庫的方法。在專案的初始化 SQLite 實現中,所有的資料庫查詢都在LocalUserDataSource中使用Cursor物件完成。使用 Room,不需要關聯程式碼所有的Cursor並且在UserDao類中使用註解簡單定義了查詢操作。

    例如,需要查詢資料庫中的所有使用者時,我們只需要寫如下程式碼,Room 就會處理所有這些繁重的任務。

    @Query("SELECT * FROM Users")
    List<User> getUsers();
    複製程式碼
  4. 建立資料庫

    到目前為止,已經定義了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();
    }
    複製程式碼

    更多關於資料庫遷移是如何實現的以及底層是如何執行得,請參見下文的文章:

  5. 在這一步,將修改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.
    複製程式碼
  6. 上機實測

    現在測試建立的UserDaoUsersDatabaseLocalUserDatabaseSource

    測試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如何使用,請參見下面的部落格文章:

  7. 刪除UserDbHelper

相關文章