前面一篇介绍了Room的对于SqliteOpenHelper 的封装,本篇就来了解一下Room 框架中数据库的框架以及对SqliteOpenHelper封装之后 的使用。
由于Room框架使用的编译时注解,会在编译期间生成很多的类,所以这先贴出一个Room的使用范例
// 定义表非常简单,只需要创建一个 class 并标记上 Entity 注解,
// 可以使用它的 `tableName` 属性声明该表的名称
@Entity(tableName = "table_cache")
class CacheTest {
// 1. 对于一个表必须存在一个不为空的主键,即必须要标记 PrimaryKey 和 NonNull 两个注解
// PrimaryKey 注解的 `autoGenerate` 属性意味该主键的值,是否由数据库自动生成
@PrimaryKey(autoGenerate = false)
@NonNull
var key: String = ""
// 2. 该字段在数据库表中的列名称,不指定的默认就等于该字段的名字
@ColumnInfo(defaultValue = "default value")
var name: String? = null
//3. 如果不想让该字段映射成表的列,可以使用该注解标记
@Ignore
var timeStamp: Long? = null
}
// 全称(data access object)
@Dao
interface CacheDaoTest { // 1. 如果是插入数据,需要标记上 Insert 注解,并指明插入数据时如果已存在一条主键一样的数据,执行的策略
// REPLACE : 直接替换老数据
// ABORT : 终止操作,并回滚事务,即老数据不影响
// IGNORE : 忽略冲突,但是会插入失败
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun saveCache(cache: CacheTest): Long
// 2. 常规查询操作,需要写 sql 语句
@Query("select * from table_cache where `key`=:primaryKey")
fun getCache(primaryKey: String): CacheTest?
// 3. 高级查询操作,可以通过 livedata 以观察者的形式获取数据库数据,可以避免不必要的 npe
// 更重要的是其可以监听数据库表中的数据的变化,一旦发生了 insert update delete
// room 会自动读取表中最新的数据,发送给 UI 层刷新页面
@Query("select * from table_cache")
fun query(): LiveData<List<CacheTest>> // 同样支持 rxjava observer
//4. 删除操作非常简单,也可以执行 sql 语句删除数据
@Delete(entity = CacheTest::class)
fun deleteCache(key: String)
//5. 更新操作,表中对应的这一行所有数据会被替换成 Cache 对象的字段值
@Update()
fun update(cache: CacheTest)
}
@Database(entities = [CacheTest::class], version = 3)
abstract class TestDb : RoomDatabase() {
abstract fun testDao(): CacheDaoTest
companion object {
val MIGRATION_2_1: Migration = object : Migration(2, 1) {
override fun migrate(database: SupportSQLiteDatabase) {
}
}
val MIGRATION_2_3: Migration = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
}
}
val MIGRATION_3_4: Migration = object : Migration(3,4) {
override fun migrate(database: SupportSQLiteDatabase) {
}
}
val MIGRATION_2_4: Migration = object : Migration(2, 4) {
override fun migrate(database: SupportSQLiteDatabase) {
}
}
}
}
关于Room的使用不是本文的重点,对于Room的使用不熟的读者可以自行百度。
上面的代码就是一个简单的Room的使用,但是需要这么创建这个数据库呢?
fun init(context: Context) {
val mDataBase = Room.databaseBuilder(
context.applicationContext,
TestDb::class.java,
"user_login_info_db"
)
.allowMainThreadQueries()
.setQueryExecutor(Executors.newSingleThreadExecutor())
.setTransactionExecutor(Executors.newSingleThreadExecutor())
.fallbackToDestructiveMigration()
.createFromAsset("assetFile")
.addMigrations(TestDb.MIGRATION_2_1)
.addMigrations(TestDb.MIGRATION_2_3)
.addMigrations(TestDb.MIGRATION_2_4).addCallback(object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
}
override fun onDestructiveMigration(db: SupportSQLiteDatabase) {
super.onDestructiveMigration(db)
}
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
}
})
.build()
}
最后我们进入build 方法看一下
public T build() {
//noinspection ConstantConditions
if (mContext == null) {
throw new IllegalArgumentException("Cannot provide null context for the database.");
}
//要创建的数据库
if (mDatabaseClass == null) {
throw new IllegalArgumentException("Must provide an abstract class that"
+ " extends RoomDatabase");
}
//事物线程池与数据库操作线程池的设置
if (mQueryExecutor == null && mTransactionExecutor == null) {
mQueryExecutor = mTransactionExecutor = ArchTaskExecutor.getIOThreadExecutor();
} else if (mQueryExecutor != null && mTransactionExecutor == null) {
mTransactionExecutor = mQueryExecutor;
} else if (mQueryExecutor == null && mTransactionExecutor != null) {
mQueryExecutor = mTransactionExecutor;
}
//检查配置冲突。例如同时配置了冲数据库1升级到数据2的迁移回调,
//同时配置了 数据库1升级到2的时候直接删除旧的数据表。
//同时配置两种策略Room框架不知道是用哪一种策略
if (mMigrationStartAndEndVersions != null && mMigrationsNotRequiredFrom != null) {
for (Integer version : mMigrationStartAndEndVersions) {
if (mMigrationsNotRequiredFrom.contains(version)) {
throw new IllegalArgumentException(
"Inconsistency detected. A Migration was supplied to "
+ "addMigration(Migration... migrations) that has a start "
+ "or end version equal to a start version supplied to "
+ "fallbackToDestructiveMigrationFrom(int... "
+ "startVersions). Start version: "
+ version);
}
}
}
//SQLiteOpenHelper 的工厂类,
//一般都是使用FrameworkSQLiteOpenHelperFactory
if (mFactory == null) {
mFactory = new FrameworkSQLiteOpenHelperFactory();
}
if (mCopyFromAssetPath != null || mCopyFromFile != null) {
//mName == null 表示数据库要建立在内存中。
if (mName == null) {
throw new IllegalArgumentException("Cannot create from asset or file for an "
+ "in-memory database.");
}
if (mCopyFromAssetPath != null && mCopyFromFile != null) {
throw new IllegalArgumentException("Both createFromAsset() and "
+ "createFromFile() was called on this Builder but the database can "
+ "only be created using one of the two configurations.");
}
mFactory = new SQLiteCopyOpenHelperFactory(mCopyFromAssetPath, mCopyFromFile,
mFactory);
}
DatabaseConfiguration configuration =
new DatabaseConfiguration(
mContext,
mName,
mFactory,
mMigrationContainer,
mCallbacks,
mAllowMainThreadQueries,
mJournalMode.resolve(mContext),
mQueryExecutor,
mTransactionExecutor,
mMultiInstanceInvalidation,
mRequireMigration,
mAllowDestructiveMigrationOnDowngrade,
mMigrationsNotRequiredFrom,
mCopyFromAssetPath,
mCopyFromFile);
//通过反射创建对应的数据,感兴趣的读者自行研究
T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
//根据配置文件初始化数据库,其实就是赋值
db.init(configuration);
return db;
}
该注释的都注释了,下面进入RoomDataBase的init方法看看
public void init(@NonNull DatabaseConfiguration configuration) {
//创建SQLiteOpenHelper,返回的SQLiteOpenHelper 是在编译期间动态生成的一个
//因为这个类跟我们定义的数据库里面的表结构息息相关,所以只可以在编译期间
//生成
mOpenHelper = createOpenHelper(configuration);
if (mOpenHelper instanceof SQLiteCopyOpenHelper) {
SQLiteCopyOpenHelper copyOpenHelper = (SQLiteCopyOpenHelper) mOpenHelper;
copyOpenHelper.setDatabaseConfiguration(configuration);
}
boolean wal = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
//提高读写的并发效率,感兴趣的可自行百度
wal = configuration.journalMode == JournalMode.WRITE_AHEAD_LOGGING;
mOpenHelper.setWriteAheadLoggingEnabled(wal);
}
mCallbacks = configuration.callbacks;
mQueryExecutor = configuration.queryExecutor;
mTransactionExecutor = new TransactionExecutor(configuration.transactionExecutor);
mAllowMainThreadQueries = configuration.allowMainThreadQueries;
mWriteAheadLoggingEnabled = wal;
//这个比较有意思,放到后面讲
if (configuration.multiInstanceInvalidation) {
mInvalidationTracker.startMultiInstanceInvalidation(configuration.context,
configuration.name);
}
}
createOpenHelper 创建一个RoomOpenHelper,关于这个类在上一篇文章中已经做了介绍
public final class TestDb_Impl extends TestDb {
private volatile CacheDaoTest _cacheDaoTest;
@Override
protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(3) {
@Override
public void createAllTables(SupportSQLiteDatabase _db) {
_db.execSQL("CREATE TABLE IF NOT EXISTS `table_cache` (`key` TEXT NOT NULL, `name` TEXT DEFAULT 'default value', PRIMARY KEY(`key`))");
_db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)");
_db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '94bf9b88aa2c2b474c4bd436bcde1b56')");
}
@Override
public void dropAllTables(SupportSQLiteDatabase _db) {
_db.execSQL("DROP TABLE IF EXISTS `table_cache`");
if (mCallbacks != null) {
for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
mCallbacks.get(_i).onDestructiveMigration(_db);
}
}
}
@Override
protected void onCreate(SupportSQLiteDatabase _db) {
if (mCallbacks != null) {
for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
mCallbacks.get(_i).onCreate(_db);
}
}
}
@Override
public void onOpen(SupportSQLiteDatabase _db) {
mDatabase = _db;
internalInitInvalidationTracker(_db);
if (mCallbacks != null) {
for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
mCallbacks.get(_i).onOpen(_db);
}
}
}
@Override
public void onPreMigrate(SupportSQLiteDatabase _db) {
DBUtil.dropFtsSyncTriggers(_db);
}
@Override
public void onPostMigrate(SupportSQLiteDatabase _db) {
}
@Override
protected RoomOpenHelper.ValidationResult onValidateSchema(SupportSQLiteDatabase _db) {
xxxx
return new RoomOpenHelper.ValidationResult(true, null);
}
}, "94bf9b88aa2c2b474c4bd436bcde1b56", "6ca6e7834faefcdfc4dba1794039f83d");
//
final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
.name(configuration.name)
.callback(_openCallback)
.build();
//sqliteOpenHelperFactory 一般是FrameworkSQLiteOpenHelperFactory,这一点前面
//介绍了,这里主要是通过工厂模式创建了一个SupportSQLiteOpenHelper
final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
return _helper;
}
}
TestDb_Impl 是编译期间生成的一个类,前面Room.getGeneratedImplementation 方法通过反射创建了一个实例返回给用户。
public final class FrameworkSQLiteOpenHelperFactory implements SupportSQLiteOpenHelper.Factory {
@Override
public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {
return new FrameworkSQLiteOpenHelper(
configuration.context, configuration.name, configuration.callback);
}
}
FrameworkSQLiteOpenHelper 可以看成是一个代理类,实际内容包含在其创建的一个内部类OpenHelper 里面
OpenHelper 继承了SQLiteOpenHelper , 关于SQLiteOpenHelper 相比大家就都很熟悉了。
关于SQLiteOpenHelper 与OpenHelper 我们在前面一篇文章已经做了完整的介绍,这里不再赘言。
下一篇文章介绍Room的增删改查,这里会着重介绍Room与LiveData的结合,我觉得这也是Room架构最牛逼的一点。这一点用好了会给我们的开发带来极大的便利。