Room 的内部基本实现
Room在编译期通过kapt处理@Dao和@Database注解,并生成DAO和Database的实现类,实现一个简单的例子:
Database:
定义一个UserDatabase,只有一个实体User:
@Database(entities = [User::class], version = 1)
abstract class UserDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
Entity: User
User有三个字段(Column):
@Entity
data class User(
@PrimaryKey val uid: Int,
@ColumnInfo(name = "first_name") val firstName: String?,
@ColumnInfo(name = "last_name") val lastName: String?
)
UserDao
接口定义 UserDao:
@Dao
interface UserDao {
@Query("SELECT * FROM user")
fun getAll(): List<User>
@Insert
fun insertAll(vararg users: User)
@Delete
fun delete(user: User)
}
**kapt **生成的代码在 build/generated/source/kapt/中生成了UserDatabase_Impl和UserDao_Impl:
UserDatabase_Impl:
@SuppressWarnings({"unchecked", "deprecation"})
public final class UserDatabase_Impl extends UserDatabase {
private volatile UserDao _userDao;
@Override
protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(1) {
@Override
public void createAllTables(SupportSQLiteDatabase _db) {
// impl
}
@Override
public void dropAllTables(SupportSQLiteDatabase _db) {
// impl
}
@Override
protected void onCreate(SupportSQLiteDatabase _db) {
// impl
}
@Override
public void onOpen(SupportSQLiteDatabase _db) {
}
@Override
public void onPreMigrate(SupportSQLiteDatabase _db) {
// impl
}
@Override
public void onPostMigrate(SupportSQLiteDatabase _db) {
// impl
}
@Override
protected InvalidationTracker createInvalidationTracker() {
final HashMap<String, String> _shadowTablesMap = new HashMap<String, String>(0);
HashMap<String, Set<String>> _viewTables = new HashMap<String, Set<String>>(0);
return new InvalidationTracker(this, _shadowTablesMap, _viewTables, "User");
}
@Override
public void clearAllTables() {
super.assertNotMainThread();
// impl
}
@Override
public UserDao userDao() {
// impl
}
}
- createOpenHelper:
Room.databaseBuilder().build()创建Database时,会调用实现类的createOpenHelper()创建SupportSQLiteOpenHelper,此Helper用来创建DB以及管理版本 - createInvalidationTracker :创建跟踪器,确保table的记录修改时能通知到相关回调方
- clearAllTables:清空table的实现
- userDao:创建
UserDao_Impl
User_Impl:
@SuppressWarnings({"unchecked", "deprecation"})
public final class UserDao_Impl implements UserDao {
private final RoomDatabase __db;
private final EntityInsertionAdapter<User> __insertionAdapterOfUser;
private final EntityDeletionOrUpdateAdapter<User> __deletionAdapterOfUser;
public UserDao_Impl(RoomDatabase __db) {
this.__db = __db;
this.__insertionAdapterOfUser = new EntityInsertionAdapter<User>(__db) {
// Impl
};
this.__deletionAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
// Impl
};
}
@Override
public void insertAll(final User... users) {
// impl
}
@Override
public void delete(final User user) {
// impl
}
@Override
public List<User> getAll() {
// impl
}
public static List<Class<?>> getRequiredConverters() {
return Collections.emptyList();
}
}
UserDao_Impl 主要有三个属性:
__db:RoomDatabase的实例__insertionAdapterOfUser:EntityInsertionAdapterd实例,用于数据 insert。上例中,将在installAll()中调用__deletionAdapterOfUser:EntityDeletionOrUpdateAdapter实例,用于数据的 update/delete。 上例中,在delete()中调用
RoomDatabase.Builder
Room通过Build模式创建Database实例
val userDatabase = Room.databaseBuilder(
applicationContext,
UserDatabase::class.java,
"users-db"
).build()
Builder的好处时便于对Database进行配置
- createFromAsset()/createFromFile() :从SD卡或者Asset的db文件创建RoomDatabase实例
- addMigrations() :添加一个数据库迁移(migration),当进行数据版本升级时需要
- allowMainThreadQueries() :允许在UI线程进行数据库查询,默认是不允许的
- fallbackToDestructiveMigration() :如果找不到migration则重建数据库表(会造成数据丢失)
除上面以外,还有其他很多配置。调用build()后,创建UserDatabase_Impl,并调用init(),内部会调用createOpenHelper()。
UserDao:
@Override
public UserDao userDao() {
if (_userDao != null) {
return _userDao;
} else {
synchronized(this) {
if(_userDao == null) {
_userDao = new UserDao_Impl(this);
}
return _userDao;
}
}
}
重写userDao(),向RoomDatabase传入 UserDao_Impl
insertAll()
@Override
public void insertAll(final User... users) {
__db.assertNotSuspendingTransaction();
__db.beginTransaction();
try {
__insertionAdapterOfUser.insert(users);
__db.setTransactionSuccessful();
} finally {
__db.endTransaction();
}
}
使用 __db开启事务,使用__insertionAdapterOfUser执行插入操作
delete()
@Override
public void delete(final User user) {
__db.assertNotSuspendingTransaction();
__db.beginTransaction();
try {
__deletionAdapterOfUser.handle(user);
__db.setTransactionSuccessful();
} finally {
__db.endTransaction();
}
}
使用 __db开启事务,使用__deletionAdapterOfUser执行删除
getAll()
@Override
public List<User> getAll() {
final String _sql = "SELECT * FROM user";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
__db.assertNotSuspendingTransaction();
final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
final int _cursorIndexOfFirstName = CursorUtil.getColumnIndexOrThrow(_cursor, "first_name");
final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "last_name");
final List<User> _result = new ArrayList<User>(_cursor.getCount());
while(_cursor.moveToNext()) {
final User _item;
final int _tmpUid;
_tmpUid = _cursor.getInt(_cursorIndexOfUid);
final String _tmpFirstName;
if (_cursor.isNull(_cursorIndexOfFirstName)) {
_tmpFirstName = null;
} else {
_tmpFirstName = _cursor.getString(_cursorIndexOfFirstName);
}
final String _tmpLastName;
if (_cursor.isNull(_cursorIndexOfLastName)) {
_tmpLastName = null;
} else {
_tmpLastName = _cursor.getString(_cursorIndexOfLastName);
}
_item = new User(_tmpUid,_tmpFirstName,_tmpLastName);
_result.add(_item);
}
return _result;
} finally {
_cursor.close();
_statement.release();
}
}
基于@Query注解的sql语句创建RoomSQLiteQuery,然后创建cursor进行后续操作
数据库升级
升级
当数据库的表结构发生变化时,我们需要通过数据库迁移(Migrations)升级表结构,避免数据丢失。
例如,我们想要为User表增加age字段
| uid | first_name | last_name |
⏬
| uid | first_name | last_name | age |
数据迁移需要使用Migration类:
val MIGRATION_1_2 = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE user ADD COLUMN age INTEGER")
}
}
Migration通过startVersion和endVersion表明当前是哪个版本间的迁移,然后在运行时,按照版本顺序调用各Migration,最终迁移到最新的Version
创建Database时设置Migration:
Room.databaseBuilder(
applicationContext,
UserDatabase::class.java,
"user"
).addMigrations(MIGRATION_1_2)
.build()
失败的升级:
迁移中如果找不到对应版的Migration,会抛出IllegalStateException:
java.lang.IllegalStateException: A migration from 1 to 2 is necessary. Please provide a Migration in the builder or call fallbackToDestructiveMigration in the builder in which case Room will re-create all of the tables.
避免升级失败导致crash:
Room.databaseBuilder(
applicationContext,
UserDatabase::class.java,
"users-db"
).fallbackToDestructiveMigration()
.build()
- fallbackToDestructiveMigration:迁移失败时,重建数据库表
- fallbackToDestructiveMigrationFrom:迁移失败时,基于某版本重建数据库表
- fallbackToDestructiveMigrationOnDowngrade:迁移失败,数据库表降级到上一个正常版本
Dao 配合第三方库使用
作为Jetpack生态的成员,Room可以很好地兼容Jetpack的其他组件, 例如LiveData、RxJava、 协程。
LiveData
DAO可以定义LiveData类型的结果,Room内部兼容了LiveData的响应式逻辑。通常的Query需要命令式的获取结果,LiveData可以让结果的更新可被观察(Observable Queries):
@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun getAllLiveData(): LiveData<List<User>>
}
当DB的数据发生变化时,Room会更新LiveData:
@Override
public LiveData<List<User>> getAllLiveData() {
final String _sql = "SELECT * FROM users";
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
return __db.getInvalidationTracker().createLiveData(new String[]{"users"}, false, new Callable<List<User>>() {
@Override
public List<User> call() throws Exception {
final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
try {
final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
final int _cursorIndexOfFirstName = CursorUtil.getColumnIndexOrThrow(_cursor, "first_name");
final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "last_name");
final List<User> _result = new ArrayList<User>(_cursor.getCount());
while(_cursor.moveToNext()) {
final User _item;
final int _tmpUid;
_tmpUid = _cursor.getInt(_cursorIndexOfUid);
final String _tmpFirstName;
_tmpFirstName = _cursor.getString(_cursorIndexOfFirstName);
final String _tmpLastName;
_tmpLastName = _cursor.getString(_cursorIndexOfLastName);
_item = new User(_tmpUid,_tmpFirstName,_tmpLastName);
_result.add(_item);
}
return _result;
} finally {
_cursor.close();
}
}
@Override
protected void finalize() {
_statement.release();
}
});
}
__db.getInvalidationTracker().createLiveData() 接受3个参数
- tableNames:被观察的表
- inTransaction:查询是否基于事务
- computeFunction:表记录变化时的回调
computeFunction的call中执行真正的sql查询。当Observer首次订阅LiveData时,或者表数据发生变化时,便会执行到这里。
Coroutine
为UserDao中的CURD方法添加suspend
@Dao
interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUsers(vararg users: User)
@Update
suspend fun updateUsers(vararg users: User)
@Delete
suspend fun deleteUsers(vararg users: User)
@Query("SELECT * FROM users")
suspend fun loadAllUsers(): Array<User>
}
@Override
public Object insertUsers(final User[] users, final Continuation<? super Unit> p1) {
return CoroutinesRoom.execute(__db, true, new Callable<Unit>() {
@Override
public Unit call() throws Exception {
__db.beginTransaction();
try {
__insertionAdapterOfUser.insert(users);
__db.setTransactionSuccessful();
return Unit.INSTANCE;
} finally {
__db.endTransaction();
}
}
}, p1);
}
CoroutinesRoom.execute 中进行真正的sql语句,并通过Continuation将callback变为Coroutine的同步调用。对比一下普通版本的insertUsers:
@Override
public void insertUsers(final User... users) {
__db.assertNotSuspendingTransaction();
__db.beginTransaction();
try {
__insertionAdapterOfUser.insert(users);
__db.setTransactionSuccessful();
} finally {
__db.endTransaction();
}
}
添加了suspend后,生成代码中会使用CoroutinesRoom.execute封装协程。