Android官方Room配合RxJava接入及使用經(jīng)驗

Room持久性庫在SQLite上提供了一個抽象層溃列,幫助開發(fā)者更友好、流暢的訪問SQLite數(shù)據(jù)庫。
以下是Room的初步使用經(jīng)驗笋除,可供基礎(chǔ)功能的使用。完整的文檔請參考https://developer.android.com/training/data-storage/room

一炸裆、添加依賴

// room
implementation "androidx.room:room-runtime:2.2.6"
annotationProcessor "androidx.room:room-compiler:2.2.6"
// optional - RxJava support for Room
implementation "androidx.room:room-rxjava2:2.2.6"

//rxAndroid
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'

二垃它、創(chuàng)建Entity

  • 一個Entity類對應一個表結(jié)構(gòu)。
  • 數(shù)據(jù)模型中的每個字段都會映射為數(shù)據(jù)庫表中的一列烹看。
  • 必須為類添加@Entity注解国拇,為主鍵添加@PrimaryKey注解。
  • @Entity - 類注解
    tableName:用于指定表名惯殊,未指定時使用類名作為表名酱吝。
  • @PrimaryKey - 字段注解:表明該字段是主鍵,一個類中一定要有一個主鍵土思。
    autoGenerate:設(shè)置主鍵自增务热,自增主鍵必須為int型忆嗜。
  • @ColumnInfo - 字段注解
    name:設(shè)置字段在表中的列名,未指定時使用字段名稱崎岂。
  • @Ignore - 字段注解:不需要存儲在數(shù)據(jù)庫中的字段可以使用該注解捆毫。
  • 數(shù)據(jù)模型需要添加set、get方法供room使用该镣。

三冻璃、創(chuàng)建Dao

  • Dao定義為一個接口。
  • 必須為類添加@Dao注解损合,在每個需要訪問數(shù)據(jù)庫中的方法上添加@Insert省艳、@Delete、@Update嫁审、@Query注解跋炕。
  • 一般情況下,對于同一個表的數(shù)據(jù)庫操作會放到一個Dao類中律适。
  • @Dao - 類注解
  • @Insert - 方法注解
    將傳入的數(shù)據(jù)插入到數(shù)據(jù)庫表中辐烂。插入數(shù)據(jù)的id會放在列表中返回。
    @Insert
    Single<List<Long>> insert(Product... products);
    
  • @Delete - 方法注解
    刪除數(shù)據(jù)庫表中的數(shù)據(jù)捂贿,會按照主鍵查找刪除纠修,會返回成功刪除的數(shù)目。
    @Delete
    Single<Integer> delete(Product... products);
    
  • @Update - 方法注解
    根據(jù)主鍵更新表中數(shù)據(jù)厂僧,會返回成功更新的數(shù)目扣草。
    @Update
    Single<Integer> update(Product... products);
    
  • @Query - 方法注解
    查詢數(shù)據(jù)庫的注解,需要自己書寫sql語句颜屠。
    此處推薦使用Flowable辰妙,在數(shù)據(jù)更新時會自動觸發(fā)查詢,但要在頁面關(guān)閉的時候及時注銷甫窟,F(xiàn)lowable默認在子線程查詢密浑,不需要手動切換線程。
    @Query("select * from product")
    Flowable<List<Product>> getAll();
    
    需要傳入?yún)?shù)進行查詢的時候可以使用“:id”占位符粗井。
    @Query("select * from product where id=:id")
    Flowable<Product> findById(int id);
    
    @Query("select * from product where name like :name")
    Flowable<List<Product>> findByName(String name);
    

四尔破、創(chuàng)建DataBase

  • 此類為抽象類,需要繼承自androidx.room.RoomDatabase
  • 必須為類添加@Database注解浇衬。
  • 對于需要暴露的Dao懒构,請?zhí)砑荧@取對應Dao的抽象方法。
    public abstract ProductDao productDao();
    
  • 建議使用工具類或在本類實現(xiàn)單例模式供整個App使用径玖,大量創(chuàng)建DataBase會消耗一定的資源。
  • @Database - 類注解
    version:int型的版本號颤介,用于標識數(shù)據(jù)庫的版本梳星。在數(shù)據(jù)庫升級的時候需要增加version的數(shù)字赞赖。
    entities:該字段為Class<?>[]類型,需要將數(shù)據(jù)庫中所包含表的Entity類添加到注解中冤灾。entities的格式為“entities = {Product.class}”前域。
    exportSchema:控制編譯器是否輸出數(shù)據(jù)庫結(jié)構(gòu)文件,默認為true韵吨,需要配置room.schemaLocation字段才能看到輸出結(jié)果匿垄。
    配置方法:
    在App級別的build.gradle文件中添加數(shù)據(jù)庫結(jié)構(gòu)導出配置,添加配置信息后重新編譯工程归粉,工程中的database目錄下會生成當前版本數(shù)據(jù)庫的表結(jié)構(gòu)椿疗。(文件名為1.json)。
    android {
        // ...
        defaultConfig {
            // ...
            javaCompileOptions {
                annotationProcessorOptions {
                    arguments += ["room.schemaLocation": "$projectDir/database".toString()]
                }
            }
        }
    }
    

五糠悼、 使用

  1. 構(gòu)建Database單例
    private static final String DB_NAME = "localData.db";
    private static volatile AppDatabase INSTANCE;
    
    public static AppDatabase getInstance(Context context) {
        if (INSTANCE == null) {
            synchronized (AppDatabase.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, DB_NAME).build();
                }
            }
        }
        return INSTANCE;
    }
    
  2. 進行數(shù)據(jù)插入
    由于Entity中的主鍵設(shè)置了自增届榄,所以在插入數(shù)據(jù)的時候不需要設(shè)置id。
    Product product1 = new Product();
    product1.setJdID("jdId_1 by insert");
    product1.setName("name_1 by insert");
    Product product2 = new Product();
    product2.setJdID("jdId_2 by insert");
    product2.setName("name_2 by insert");
    Disposable disposable = AppDatabase.getInstance(this).productDao().insert(product1, product2)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Consumer<List<Long>>() {
                @Override
                public void accept(List<Long> ids) throws Exception {
                    System.out.println("insert number = " + ids.size());
                    for (long id : ids) {
                        System.out.println("insert id = " + id);
                    }
                }
            }, new Consumer<Throwable>() {
                @Override
                public void accept(Throwable throwable) throws Exception {
                    System.out.println("insert error = " + throwable.getMessage());
                    throwable.printStackTrace();
                }
            });
    
  3. 刪除數(shù)據(jù)
    刪除數(shù)據(jù)的接口使用的是@Delete注解倔喂,自動生成的方法僅支持根據(jù)傳入對象的主鍵進行刪除铝条,如果需要使用其他刪除條件,建議使用@Query注解并手動編寫sql語句席噩。
    Product product = new Product();
    product.setId(6);
    Disposable disposable = AppDatabase.getInstance(this).productDao().delete(product)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Consumer<Integer>() {
                @Override
                public void accept(Integer integer) throws Exception {
                    System.out.println("delete number = " + integer);
                }
            }, new Consumer<Throwable>() {
                @Override
                public void accept(Throwable throwable) throws Exception {
                    System.out.println("delete error = " + throwable.getMessage());
                    throwable.printStackTrace();
                }
            });
    
  4. 修改數(shù)據(jù)
    修改數(shù)據(jù)的接口使用的是@Update注解班缰,自動生成的方法僅支持根據(jù)傳入對象的主鍵進行更新,如果需要使用其他更新條件悼枢,建議使用@Query注解并手動編寫sql語句埠忘。
    Product product = new Product();
    product.setId(2);
    product.setName("name by update");
    Disposable disposable = AppDatabase.getInstance(this).productDao().update(product)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Consumer<Integer>() {
                @Override
                public void accept(Integer integer) throws Exception {
                    System.out.println("update number = " + integer);
                }
            }, new Consumer<Throwable>() {
                @Override
                public void accept(Throwable throwable) throws Exception {
                    System.out.println("update error = " + throwable.getMessage());
                    throwable.printStackTrace();
                }
            });
    
  5. 查詢數(shù)據(jù)
    查詢數(shù)據(jù)方法返回的是一個Flowable,在對應表更新數(shù)據(jù)之后會重新進行查詢并返回結(jié)果萧芙。
    由于數(shù)據(jù)更新的單位是整張表给梅,更新的數(shù)據(jù)不在查詢范圍內(nèi)時依舊會重新查詢并返回結(jié)果,可以使用distinctUntilChanged操作符將連續(xù)重復的數(shù)據(jù)過濾掉双揪。
    Disposable disposable = AppDatabase.getInstance(this).productDao().getAll()
            .distinctUntilChanged()
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Consumer<List<Product>>() {
                @Override
                public void accept(List<Product> products) throws Exception {
                    System.out.println("query number = " + products.size());
                    for (Product product : products) {
                        System.out.println("query = " + product);
                    }
                }
            }, new Consumer<Throwable>() {
                @Override
                public void accept(Throwable throwable) throws Exception {
                    System.out.println("query error = " + throwable.getMessage());
                    throwable.printStackTrace();
                }
            });
    
  6. 檢查
    以上方法返回的Disposable都未進行處理动羽,數(shù)據(jù)庫操作是在異步線程執(zhí)行的,為防止回調(diào)時出現(xiàn)錯誤渔期,請將返回的disposable與頁面生命周期進行綁定运吓,及時終止操作。

六疯趟、數(shù)據(jù)庫升級

數(shù)據(jù)庫的表結(jié)構(gòu)很難一次性定義完整拘哨,有時會根據(jù)業(yè)務的變化而修改數(shù)據(jù)庫的表結(jié)構(gòu),修改表結(jié)構(gòu)就要對數(shù)據(jù)庫進行升級信峻,升級的流程如下:

  1. 創(chuàng)建或修改Entity
    現(xiàn)在就可以根據(jù)業(yè)務需要調(diào)整數(shù)據(jù)庫中的表結(jié)構(gòu)了倦青,由于表結(jié)構(gòu)是映射為Entity類,所以按照需求調(diào)整Entity類就好了盹舞。
  2. 修改@Database注解中的參數(shù)
    • 升級版本號
    • 更新entities中的參數(shù)产镐,使其與新版本的表結(jié)構(gòu)一致隘庄。
  3. 創(chuàng)建Migration類
    • 創(chuàng)建一個繼承自Migration的類或直接實現(xiàn)一個Migration匿名類。
    • 在構(gòu)造函數(shù)中指定數(shù)據(jù)庫升級時舊的版本號和新的版本號癣亚。
    • 實現(xiàn)migrate方法丑掺,migrate方法中會附帶一個database參數(shù),直接通過database.execSQL("")方法執(zhí)行數(shù)據(jù)庫表的更新語句述雾。以下列舉幾個常見的數(shù)據(jù)庫更新Migration街州。
      添加新表
      private static class Migration1_2 extends Migration {
      
          public Migration1_2() {
              super(1, 2);
          }
      
          @Override
          public void migrate(@NonNull SupportSQLiteDatabase database) {
              database.execSQL("CREATE TABLE IF NOT EXISTS 'log' ('id' INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, 'time' INTEGER NOT NULL, 'code' INTEGER NOT NULL, 'message' TEXT)");
          }
      }
      
      添加字段
      private static class Migration2_3 extends Migration {
      
          public Migration2_3() {
              super(2, 3);
          }
      
          @Override
          public void migrate(@NonNull SupportSQLiteDatabase database) {
              database.execSQL("alter table 'product' add 'enable' integer not null default '1'");
          }
      }
      
      修改表名:我之前創(chuàng)建的表名是Product,希望修改成product玻孟。
      private static class Migration3_4 extends Migration {
      
          public Migration3_4() {
              super(3, 4);
          }
      
          @Override
          public void migrate(@NonNull SupportSQLiteDatabase database) {
              // 數(shù)據(jù)庫中的表名在使用時不區(qū)分大小寫唆缴,可在展示的時候是有大寫字母的,所以在修改表名的時候先轉(zhuǎn)換成一個臨時的表名取募。
              database.execSQL("alter table 'Product' rename to 'product_tmp'");
              database.execSQL("alter table 'product_tmp' rename to 'product'");
          }
      }
      
  4. 將自定義的Migration對象通過addMigrations方法添加到數(shù)據(jù)庫初始化方法中琐谤,此方法可以添加不定數(shù)量的Migration并根據(jù)版本號依次執(zhí)行。
    Room.databaseBuilder(application.getApplicationContext(), AppDatabase.class, DB_NAME)
            .addMigrations(
                new Migration1_2(),
                new Migration2_3(),
                new Migration3_4())
            .build();
    
  5. 重新編譯安裝app玩敏,在使用數(shù)據(jù)庫的時候會自動根據(jù)上面的配置信息進行數(shù)據(jù)庫升級斗忌,由于數(shù)據(jù)庫升級會消耗一定的時間,第一次調(diào)用數(shù)據(jù)庫的方法時間會稍長一點旺聚。

七织阳、進階功能

對于小型的app,基本的數(shù)據(jù)庫功能就足以滿足業(yè)務需求砰粹,下面這些進階的功能暫未嘗試唧躲。

  • @ForeignKey注解
    數(shù)據(jù)庫中的外鍵,用于確定表與表的關(guān)系碱璃。
  • @TypeConverters注解
    數(shù)據(jù)庫的類型轉(zhuǎn)換器弄痹,復雜的數(shù)據(jù)結(jié)構(gòu)可以通過類型轉(zhuǎn)換器轉(zhuǎn)換成簡單的數(shù)據(jù)再存放到數(shù)據(jù)庫中。
  • @Index注解
    數(shù)據(jù)庫的索引嵌器,常用于大型的數(shù)據(jù)庫中肛真,可以減少超大數(shù)據(jù)量的查詢時間。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末爽航,一起剝皮案震驚了整個濱河市蚓让,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌讥珍,老刑警劉巖历极,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異衷佃,居然都是意外死亡趟卸,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锄列,“玉大人新蟆,你說我怎么就攤上這事∮胰铮” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵吮螺,是天一觀的道長饶囚。 經(jīng)常有香客問我,道長鸠补,這世上最難降的妖魔是什么萝风? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮紫岩,結(jié)果婚禮上规惰,老公的妹妹穿的比我還像新娘。我一直安慰自己泉蝌,他們只是感情好歇万,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著勋陪,像睡著了一般贪磺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上诅愚,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天寒锚,我揣著相機與錄音,去河邊找鬼违孝。 笑死刹前,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的雌桑。 我是一名探鬼主播喇喉,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼筹燕!你這毒婦竟也來了轧飞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤撒踪,失蹤者是張志新(化名)和其女友劉穎过咬,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體制妄,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡掸绞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片衔掸。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡烫幕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出敞映,到底是詐尸還是另有隱情较曼,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布振愿,位于F島的核電站捷犹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏冕末。R本人自食惡果不足惜萍歉,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望档桃。 院中可真熱鬧枪孩,春花似錦、人聲如沸藻肄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嘹屯。三九已至斗幼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間抚垄,已是汗流浹背蜕窿。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留呆馁,地道東北人桐经。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像浙滤,于是被迫代替她去往敵國和親阴挣。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容