Room系列专题

Android Jetpack之Room篇

Room Entity注解说明

Room Dao注解说明

Room Fts 虚拟表模块

Room DatabaseView 视图

Room SkipQueryVerification

Room TypeConverter 属性类型转换器

引用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Room components
implementation "androidx.room:room-runtime:2.2.0-rc01"
annotationProcessor "androidx.room:room-compiler:2.2.0-rc01"

// kotlin扩展和协程支持
implementation "androidx.room:room-ktx:2.2.0-rc01"
//RxJava 支持库
implementation "androidx.room:room-rxjava2:2.2.0-rc01"
// 可选 - Guava 的支持库
implementation "androidx.room:room-guava:2.2.0-rc01"

基本使用

Room 主要包含三个组件:

  • Database: 包含数据库持有者,作为与应用持久化相关数据的底层连接的主要接入点。这个类需要用 @Database 注解,并满足下面条件:
    • 必须是继承 RoomDatabase 的抽象类
    • 注解中包含该数据库相关的实体类列表
    • 包含的抽象方法不能有参数,且返回值必须是被 @Dao 注解的类
  • Entity: 表示了数据库中的一张表
  • DAO: 包含了访问数据库的一系列方法

15687184173891.jpg

简单使用流程

0.配置编译器选项

如果不配置,编译就会报错。

Room具有以下注释处理器选项:

  • room.schemaLocation:配置并启用将数据库模式导出到给定目录中的JSON文件中。
  • room.incremental:启用Gradle增量注释处理器。
  • room.expandProjection:将Room配置为重写查询,以使其顶部的星形投影扩展为仅包含DAO方法返回类型中定义的列。

代码配置如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
android {
    ...
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [
                    "room.schemaLocation":"$projectDir/schemas".toString(),
                    "room.incremental":"true",
                    "room.expandProjection":"true"]
            }
        }
    }
}

1.创建模型类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Entity(tableName = "users")
public class User {

    @NonNull
    @PrimaryKey//每个bean类都必须要声明一个主键,除非父类声明了。
    @ColumnInfo(name = "userid")
    private String mId;

    @ColumnInfo(name = "username")
    private String mUserName;

    @Ignore
    public User(String userName) {
        mId = UUID.randomUUID().toString();
        mUserName = userName;
    }

    public User(String id, String userName) {
        this.mId = id;
        this.mUserName = userName;
    }

    public String getId() {
        return mId;
    }

    public String getUserName() {
        return mUserName;
    }
}

2.Dao添加 @Dao 注解,然后在方法中添加注解

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
@Dao
public interface UserDao {

    //查询数据
    @Query("SELECT * FROM Users LIMIT 1")
    Flowable<User> getUser();

    //插入数据
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    Completable insertUser(User user);
    
    //删除数据
    @Query("DELETE FROM Users")
    void deleteAllUsers();
}

3.创建类继承 RoomDatabase ,entities 填写需要的类,然后写抽象方法获取 Dao.

1
2
3
4
5
6
7
8
/**
 * 必须是 RoomDatabase 的抽象类
 */
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao userDao();
    //...code
}

4.创建数据库,并单例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
 * 必须是 RoomDatabase 的抽象类
 */
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao userDao();
  
    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, "Sample.db")
                            .build();
                }
            }
        }
        return INSTANCE;
    }

    public static UserDao getUserDao(Context context) {
        return getInstance(context).userDao();
    }
}

5.使用

1
AppDatabase.getUserDao(context).insertUser(new User("zhang"));

参数

addMigrations()升级数据库

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
private Migration migration_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(@NonNull SupportSQLiteDatabase database) {
        database.execSQL("");
    }
};

Room.databaseBuilder(context,AppDatabase.class, "database-name")
                .addMigrations(migration_1_2)//升级数据库
                .build();

enableMultiInstanceInvalidation()

设置此 RoomDatabase 实例中的表失效是否应该广播,并与相同 RoomDatabase 的其他实例(包括单独进程中的实例)同步。为了防止启用多实例失效,必须在两端同时启用。 默认情况下不启用此功能。 这不适用于内存数据库。这在针对不同数据库文件的数据库实例之间不起作用。

addCallback()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
.addCallback(new RoomDatabase.Callback() {
    //第一次创建数据库时调用。在创建所有表之后调用这个函数。
    @Override
    public void onCreate(@NonNull SupportSQLiteDatabase db) {
    }

    //在数据库打开时调用。
    @Override
    public void onOpen(@NonNull SupportSQLiteDatabase db) {
    }

    //在数据库被破坏性迁移后调用
    @Override
    public void onDestructiveMigration(@NonNull SupportSQLiteDatabase db) {
    }
})

createFromFile() createFromAsset()

配置Room以使用预打包的数据库文件创建和打开数据库

setTransactionExecutor()

设置事务的线程处理器,默认是固定 4 大小的ExecutorService。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public class DefaultTaskExecutor extends TaskExecutor {

    private final Object mLock = new Object();

    private final ExecutorService mDiskIO = Executors.newFixedThreadPool(4, new ThreadFactory() {
        private static final String THREAD_NAME_STEM = "arch_disk_io_%d";

        private final AtomicInteger mThreadId = new AtomicInteger(0);

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            t.setName(String.format(THREAD_NAME_STEM, mThreadId.getAndIncrement()));
            return t;
        }
    });
    //...code
}

setQueryExecutor()

设置查询操作的线程处理器

setJournalMode

设置此数据库的日志模式。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
 public enum JournalMode {
        //让room选择日志模式。这是未指定显式值时的默认值。
        //当设备运行的API级别低于16或它是一个低ram设备时,实际值将是 TRUNCATE 。否则,将使用 WRITE_AHEAD_LOGGING。
        AUTOMATIC,
        //截断日志模式
        TRUNCATE,
        //写前日志记录模式
        @RequiresApi(Build.VERSION_CODES.JELLY_BEAN)
        WRITE_AHEAD_LOGGING;
}

allowMainThreadQueries() 允许在主线程查询数据

建议禁用 room 的主线程查询检查。 Room确保数据库不会在主线程上被访问,因为它可能会锁定主线程并触发ANR。如果需要从主线程访问数据库,应该始终使用异步替代方法,或者手动将调用移动到后台线程。 您可能需要关闭此复选框以进行测试。

openHelperFactory()

设置数据库工厂。如果想使用第三库数据的时候,可以自定设置。

fallbackToDestructiveMigration()

允许空间破坏性地重新创建数据库表 加上它之后升级就会清空数据库中以前的数据。一般情况下我们都是希望保留数据的,所以需要些我们自己的Migration类,定义升级的sql。

fallbackToDestructiveMigrationOnDowngrade()

当降级到旧模式版本时,如果迁移不可用,则允许Room破坏性地重新创建数据库表。

fallbackToDestructiveMigrationFrom(int…)

通知Room允许从特定的启动模式版本破坏性地重新创建数据库表。

和LiveData一起使用

添加依赖

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//lifecycle依赖
implementation "androidx.lifecycle:lifecycle-runtime:2.2.0-alpha04"
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0-alpha04"
implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0-alpha04"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-alpha04"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0-alpha04"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0-alpha04"

// ReactiveStreams support for LiveData
implementation "androidx.lifecycle:lifecycle-reactivestreams-ktx:2.1.0"

修改返回类型

1
2
3
4
5
@Dao
public interface MyDao {
    @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
    public LiveData<List<User>> loadUsersFromRegionsSync(List<String> regions);
}

和RxJava一起使用

添加依赖

1
2
// RxJava support for Room
implementation "androidx.room:room-rxjava2:2.2.0-rc01"

修改返回类型

1
2
3
4
5
@Dao
public interface MyDao {
    @Query("SELECT * from user where id = :id LIMIT 1")
    public Flowable<User> loadUserById(int id);
}

直接游标访问

1
2
3
4
5
@Dao
public interface MyDao {
    @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")
    public Cursor loadRawUsersOlderThan(int minAge);
}

如果你喜欢我的文章,可以关注我的掘金、公众号、博客、简书或者Github!

简书: https://www.jianshu.com/u/a2591ab8eed2

GitHub: https://github.com/bugyun

Blog: https://ruoyun.vip

掘金: https://juejin.im/user/56cbef3b816dfa0059e330a8/posts

CSDN: https://blog.csdn.net/zxloveooo

欢迎关注微信公众号