Jetpack Room入门系列:(一)基本介绍
Jetpack Room入门系列:(二)使用DAO读写数据库
Jetpack Room入门系列:(三)实体/数据表关系
Jetpack Room入门系列:(四)内部实现原理
Jetpack Room入门系列:(五)数据库版本升级、数据迁移
Jetpack Room入门系列:(六)配合LiveData等三方库的使用
通过例子了解一下Room的底层实现原理。
Sample
Database
定义一个UserDatabase,只有一个实体User:
@Database(entities = [User::class], version = 1)
abstract class UserDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
Entity
User有三个字段(Column):
@Entity(tableName = USERS_TABLE)
data class User(
@PrimaryKey val uid: Int,
@ColumnInfo(name = FIRST_NAME_COLUMN) val firstName: String?,
@ColumnInfo(name = LAST_NAME_COLUMN) val lastName: String?
)
UserDao
通过接口定义UserDao
@Dao
interface UserDao {
@Query("SELECT * FROM $USERS_TABLE")
fun getAll(): List<User>
@Insert
fun insertAll(vararg users: User)
@Delete
fun delete(user: User)
}
实现原理
Room在编译期通过kapt处理@Dao和@Database注解,并生成DAO和Database的实现类,UserDatabase_Impl
和UserDao_Impl
。kapt生成的代码在 build/generated/source/kapt/
UserDatabase_Impl
public final class UserDatabase_Impl extends UserDatabase {
private volatile UserDao _userDao;
//RoomDataBase的init中调用
@Override
protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(1) {
@Override
public void createAllTables(SupportSQLiteDatabase _db) {
//Implementation
}
@Override
protected void onCreate(SupportSQLiteDatabase _db) {
//Implementation
}
});
final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
.name(configuration.name)
.callback(_openCallback)
.build();
final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
return _helper;
}
@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, "users");
}
@Override
public void clearAllTables() {
super.assertNotMainThread();
final SupportSQLiteDatabase _db = super.getOpenHelper().getWritableDatabase();
try {
super.beginTransaction();
_db.execSQL("DELETE FROM `users`");
super.setTransactionSuccessful();
} finally {
super.endTransaction();
_db.query("PRAGMA wal_checkpoint(FULL)").close();
if (!_db.inTransaction()) {
_db.execSQL("VACUUM");
}
}
}
@Override
public UserDao userDao() {
//实现见后文
}
- createOpenHelper:
Room.databaseBuilder().build()
创建Database时,会调用实现类的createOpenHelper()创建SupportSQLiteOpenHelper
,此Helper用来创建DB以及管理版本 - createInvalidationTracker :创建跟踪器,确保table的记录修改时能通知到相关回调方
- clearAllTables:清空table的实现
- userDao:创建
UserDao_Impl
UserDao_Impl
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) {
//Implementation
};
this.__deletionAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
//Implementation
};
}
@Override
public void insertAll(final User... users) {
//Implementation
}
@Override
public void delete(final User user) {
//Implementation
}
@Override
public List<User> getAll() {
//Implementation
}
@Override
public List<User> loadAllByIds(final int[] userIds) {
//Implementation
}
@Override
public User findByName(final String first, final String last) {
//Implementation
}
}
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_Impl传入RoomDatabase
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();
}
}
同insertAll()
getAll()
@Override
public List<User> getAll() {
final String _sql = "SELECT * FROM users";
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;
_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();
_statement.release();
}
}
基于@Query注解的sql语句创建RoomSQLiteQuery
,然后创建cursor
进行后续操作