Android官方Room配合RxJava接入及使用经验

2021-01-26  本文已影响0人  _青_9609

Room持久性库在SQLite上提供了一个抽象层,帮助开发者更友好、流畅的访问SQLite数据库。
以下是Room的初步使用经验,可供基础功能的使用。完整的文档请参考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'

二、创建Entity

三、创建Dao

四、创建DataBase

五、 使用

  1. 构建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. 进行数据插入
    由于Entity中的主键设置了自增,所以在插入数据的时候不需要设置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. 删除数据
    删除数据的接口使用的是@Delete注解,自动生成的方法仅支持根据传入对象的主键进行删除,如果需要使用其他删除条件,建议使用@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. 修改数据
    修改数据的接口使用的是@Update注解,自动生成的方法仅支持根据传入对象的主键进行更新,如果需要使用其他更新条件,建议使用@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. 查询数据
    查询数据方法返回的是一个Flowable,在对应表更新数据之后会重新进行查询并返回结果。
    由于数据更新的单位是整张表,更新的数据不在查询范围内时依旧会重新查询并返回结果,可以使用distinctUntilChanged操作符将连续重复的数据过滤掉。
    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都未进行处理,数据库操作是在异步线程执行的,为防止回调时出现错误,请将返回的disposable与页面生命周期进行绑定,及时终止操作。

六、数据库升级

数据库的表结构很难一次性定义完整,有时会根据业务的变化而修改数据库的表结构,修改表结构就要对数据库进行升级,升级的流程如下:

  1. 创建或修改Entity
    现在就可以根据业务需要调整数据库中的表结构了,由于表结构是映射为Entity类,所以按照需求调整Entity类就好了。
  2. 修改@Database注解中的参数
    • 升级版本号
    • 更新entities中的参数,使其与新版本的表结构一致。
  3. 创建Migration类
    • 创建一个继承自Migration的类或直接实现一个Migration匿名类。
    • 在构造函数中指定数据库升级时旧的版本号和新的版本号。
    • 实现migrate方法,migrate方法中会附带一个database参数,直接通过database.execSQL("")方法执行数据库表的更新语句。以下列举几个常见的数据库更新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'");
          }
      }
      
      修改表名:我之前创建的表名是Product,希望修改成product。
      private static class Migration3_4 extends Migration {
      
          public Migration3_4() {
              super(3, 4);
          }
      
          @Override
          public void migrate(@NonNull SupportSQLiteDatabase database) {
              // 数据库中的表名在使用时不区分大小写,可在展示的时候是有大写字母的,所以在修改表名的时候先转换成一个临时的表名。
              database.execSQL("alter table 'Product' rename to 'product_tmp'");
              database.execSQL("alter table 'product_tmp' rename to 'product'");
          }
      }
      
  4. 将自定义的Migration对象通过addMigrations方法添加到数据库初始化方法中,此方法可以添加不定数量的Migration并根据版本号依次执行。
    Room.databaseBuilder(application.getApplicationContext(), AppDatabase.class, DB_NAME)
            .addMigrations(
                new Migration1_2(),
                new Migration2_3(),
                new Migration3_4())
            .build();
    
  5. 重新编译安装app,在使用数据库的时候会自动根据上面的配置信息进行数据库升级,由于数据库升级会消耗一定的时间,第一次调用数据库的方法时间会稍长一点。

七、进阶功能

对于小型的app,基本的数据库功能就足以满足业务需求,下面这些进阶的功能暂未尝试。

上一篇 下一篇

猜你喜欢

热点阅读