Android Jetpack组件之数据库Room详解(二)

本文涉及Library的版本如下:

  • androidx.room:room-runtime:2.1.0-alpha03
  • androidx.room:room-compiler:2.1.0-alpha03(注解编译器)

回顾一下安卓的SQLiteOpenHelper相关类

首先放一个关于安卓数据库的类图:

android_sqlite

SQLiteOpenHelper是一个抽象类,通常自己实现数据库,需要继承SQLiteOpenHelper, 在OnCreate()里建表,在onUpgrade()处理版本迁移等。SQLiteOpenHelper是个帮助类,里面SQLiteDatabase类才是真正代表一个数据库。SQLiteDatabase提供了打开数据库,增删改查等方法。我们通常是执行sql语句去操作数据库,只不过官方封装好更方便使用。

上图中SQLiteProgram是抽象类,是编译SQLite程序的基类。成员变量里有sql语句、表字段名数据,相对应的字段的值。SQLiteStatement继承SQLiteProgram, 提供一下执行语句的方法。SQLiteQurey也是继承SQLiteProgram,代表了查询的执行操作。安卓的数据库操作把查询操作和其他操作分开。通过SQLiteDirectCursorDriver驱动执行SQLiteQurey生成SQLiteCursor游标来去数据; 建表,删表,建索引等是通过SQLiteStatement.excute()执行; 更新和删除通过SQLiteStatement.executeUpdateDelete()执行; 插入数据通过SQLiteStatement.executeInsert()。Room在原有的基础上进行了封装。

Room的类图结构

room_classes

上图有一些Support开头的接口, 这些接口存在androidx.sqlite:sqlite:2.0.0库里, 这个是对安卓原有数据库操作做了接口的抽离。SupportSQLiteDatabase对应SQLiteDatabase,、SupportSQLiteOpenHelper对应SQLiteOpenHelper、SupportSQLiteProgram对应SQLiteProgram等等;

Framework开头一些类的是对一些Support接口的实现;Framework开头这些类存在于androidx.sqlite:sqlite-framework:2.0.0库中, FrameworkSQLiteDatabase实现里有个成员变量SQLiteDatabase,实现的接口都是交给SQLiteDatabase处理。FrameworkSQLiteOpenHelper、FrameworkSQLiteProgram、FrameworkSQLiteStatement都是这个套路,使用装饰者模式,真正的实现还是安卓原有数据库操作的类。FrameworkSQLiteOpenHelperFactory工厂返回得是FrameworkSQLiteOpenHelper.OpenHelper类,FrameworkSQLiteOpenHelper.OpenHelper继承SQLiteOpenHelper。

Room开头这些类存在androidx.room:room-runtime:2.10库中, 这个库基于Support这类接口(例如:SupportSQLiteDatabase)和Framework实现(FrameworkSQLiteDatabase的实现)再次封装。room使用过程中需要定义一个抽象AppDatabase继承RoomDatabase,使用Room.databaseBuilder()去生成AppDatabase的实现。

Room数据库表的创建

AppDatabase是一个抽象类,真正的实现是AppDatabase_Impl, AppDatabase_Impl是用编译时注解生成的,注解编译器是androidx.room:room-compiler:$room_version。AppDatabase实现类是由RoomDatabase.Builder.build()创建的,先看build方法的实现:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public T build() {
......
if (mFactory == null) { // SQLiteOpenHelper工厂
mFactory = new FrameworkSQLiteOpenHelperFactory();
}
//DatabaseConfiguration是数据配置类,
//存储context, 数据库名,SQLiteOpenHelperFactory等参数
DatabaseConfiguration configuration =
new DatabaseConfiguration(mContext, mName, mFactory, mMigrationContainer,
mCallbacks, mAllowMainThreadQueries, mJournalMode.resolve(mContext),
mQueryExecutor,
mMultiInstanceInvalidation,
mRequireMigration,
mAllowDestructiveMigrationOnDowngrade, mMigrationsNotRequiredFrom);
//DB_IMPL_SUFFIX = "_Impl", db的实现是AppDatabase_Impl
T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
//init方法实现在RoomDatabase
db.init(configuration);
return db;
}

static <T, C> T getGeneratedImplementation(Class<C> klass, String suffix) {
final String fullPackage = klass.getPackage().getName();
String name = klass.getCanonicalName();
final String postPackageName = fullPackage.isEmpty()
? name
: (name.substring(fullPackage.length() + 1));
final String implName = postPackageName.replace('.', '_') + suffix;
// klass的全包名 + "_Impl",反射调用newInstance()生成实例
final Class<T> aClass = (Class<T>) Class.forName(
fullPackage.isEmpty() ? implName : fullPackage + "." + implName);
return aClass.newInstance();
}

//RoomDatabase.init方法
public void init(@NonNull DatabaseConfiguration configuration) {
//建表
mOpenHelper = createOpenHelper(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;
// 查询Executor:决定查询执行的线程
mQueryExecutor = configuration.queryExecutor;
mAllowMainThreadQueries = configuration.allowMainThreadQueries;
mWriteAheadLoggingEnabled = wal;
if (configuration.multiInstanceInvalidation) {
mInvalidationTracker.startMultiInstanceInvalidation(configuration.context,
configuration.name);
}
}

RoomDatabase.createOpenHelper方法是抽象方法,实现在AppDatabase_Impl, 定位到AppDatabase_Impl.createOpenHelper方法

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
  @Override
protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
//_openCallback是局部匿名内部实例, 是一个RoomOpenHelper实例, 先不管这个实例,接着看下面。
final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(1) {
@Override
public void createAllTables(SupportSQLiteDatabase _db) {
//执行建表语句
_db.execSQL("CREATE TABLE IF NOT EXISTS `User` (`first_name` TEXT, `name` TEXT, `id` INTEGER NOT NULL, PRIMARY KEY(`id`))");
}

@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);
}
}
}

}, "e216f2ddb0b894667088e1e7fec58cdd", "07bca20d2ba295fc9d4acbe7a3f64d4b");

//SupportSQLiteOpenHelper.Configuration数据库相关配置
//存储了context, name(数据库名字),SupportSQLiteOpenHelper.Callback对象
final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
.name(configuration.name)
.callback(_openCallback)
.build();
//工厂生成SupportSQLiteOpenHelper, 这里的工厂默认是FrameworkSQLiteOpenHelperFactory
//默认值在RoomDatabase.build()里赋值
final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
return _helper;
}

//FrameworkSQLiteOpenHelperFactory类的实现
public final class FrameworkSQLiteOpenHelperFactory implements SupportSQLiteOpenHelper.Factory {
@Override
public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {
//new 一个FrameworkSQLiteOpenHelper, 接着看FrameworkSQLiteOpenHelper的构造器
//获取SupportSQLiteOpenHelper.Configuration的context, name, callback
return new FrameworkSQLiteOpenHelper(
configuration.context, configuration.name, configuration.callback);
}
}

//FrameworkSQLiteOpenHelper的构造器
FrameworkSQLiteOpenHelper(Context context, String name,
Callback callback) {
mDelegate = createDelegate(context, name, callback);
}
//FrameworkSQLiteOpenHelper.createDelegate方法
private OpenHelper createDelegate(Context context, String name, Callback callback) {
final FrameworkSQLiteDatabase[] dbRef = new FrameworkSQLiteDatabase[1];
//OpenHelper是FrameworkSQLiteOpenHelper的内部类
return new OpenHelper(context, name, dbRef, callback);
}

//OpenHelper继承安卓的SQLiteOpenHelper
static class OpenHelper extends SQLiteOpenHelper {
final FrameworkSQLiteDatabase[] mDbRef;
final Callback mCallback;
// see b/78359448
private boolean mMigrated;

OpenHelper(Context context, String name, final FrameworkSQLiteDatabase[] dbRef,
final Callback callback) {
super(context, name, null, callback.version,
new DatabaseErrorHandler() {
@Override
public void onCorruption(SQLiteDatabase dbObj) {
FrameworkSQLiteDatabase db = dbRef[0];
if (db != null) {
callback.onCorruption(db);
}
}
});
mCallback = callback;
mDbRef = dbRef;
}

@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
//数据库初始化, mCallback是RoomOpenHelper
mCallback.onCreate(getWrappedDb(sqLiteDatabase));
}
}

//RoomOpenHelper.onCreate方法
@Override
public void onCreate(SupportSQLiteDatabase db) {
//更新Identity(这里跳过,不深入)
updateIdentity(db);
//mDelegate是RoomOpenHelper的内部类Delegate, Delegate是一个抽象类,
//定义了createAllTables, onCreate, onOpen等方法
//mDelegate在这里实现是AppDatabase_Impl.createOpenHelper方法里_openCallback实例

//调用建表方法
mDelegate.createAllTables(db);
//调用onCreate方法
mDelegate.onCreate(db);

//回看_openCallback实例的实现, createAllTables里执行了建表语句
}

Room数据库插入操作过程

Room数据访问只需要定义一个DAO接口, 在Dao接口里定义方法以及注解即可。

1
2
3
4
5
6
7
8
@Dao
public interface UserDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(User user);
}

//调用
AppDatabase.getInstance().userDao().insert(user);

前面有说到AppDatabase实现类AppDatabase_Impl, 而这里UserDao的实现类是UserDao_Impl。UserDao_Impl也是编译时自动生成实现类,先看一下AppDatabase_Impl.userDao()

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public UserDao userDao() {
if (_userDao != null) {
return _userDao;
} else {
synchronized(this) {
if(_userDao == null) {
_userDao = new UserDao_Impl(this);
}
return _userDao;
}
}
}

接口UserDao_Impl的实现

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
31
32
33
34
35
36
37
38
39
40
//UserDao_Impl构造器
public UserDao_Impl(RoomDatabase __db) {
this.__db = __db;
// 创建一个EntityInsertionAdapter对象
this.__insertionAdapterOfUser = new EntityInsertionAdapter<User>(__db) {

//插入的数据的sql语句
@Override
public String createQuery() {
return "INSERT OR ABORT INTO `User`(`first_name`,`name`,`id`) VALUES (?,?,?)";
}

//绑定插入的字段,对应上面sql语句的‘?’占位符
@Override
public void bind(SupportSQLiteStatement stmt, User value) {
if (value.firstName == null) {
stmt.bindNull(1); // 对应第一个“?”占位符,代表更新first_name的值
} else {
stmt.bindString(1, value.firstName);
}
if (value.name == null) {
stmt.bindNull(2);
} else {
stmt.bindString(2, value.name);
}
stmt.bindLong(3, value.id);
}
};
}

@Override
public void insert(final User user) {
__db.beginTransaction();
try { // 开启事务,进行插入数据
__insertionAdapterOfUser.insert(user);
__db.setTransactionSuccessful();
} finally {
__db.endTransaction();
}
}

从上面的代码可以知道,最后开启一个事务,调用EntityInsertionAdapter.insert方法进行数据插入, EntityInsertionAdapter是一个抽象类,利用泛型封装了支持所有类型的数据插入,实现类必须要实现createQuery和bind两个方法。createQuery方法返回一条sql语句,而bind方法是对数据类进行绑定,和sql语句是一一对应的。

EntityInsertionAdapter.insert方法如下:

1
2
3
4
5
6
7
8
9
public final void insert(T entity) {
final SupportSQLiteStatement stmt = acquire(); //创建一个SupportSQLiteStatement, 这里会调用到createQuery方法
try {
bind(stmt, entity);//会调用bind(SupportSQLiteStatement stmt, T value)
stmt.executeInsert();//执行sql语句
} finally {
release(stmt);
}
}

最终数据类的插入,通过SupportSQLiteStatement接口去执行sql语句。看回本篇文章开头的类结构图,SupportSQLiteStatement是一个接口, 实现是FrameworkSQLiteStatement, 而FrameworkSQLiteStatement内部实现是有一个委派者SQLiteStatement去执行的,最终也是调用安卓原有SQLiteStatement类去执行。

Room数据库查询过程

查询也类似,通过定义一个Dao接口和@Query注解,大致代码如下:

1
2
3
4
5
@Dao
public interface UserDao {
@Query("SELECT * FROM user WHERE id = :id")
User findById(String id);
}

UserDao_Impl.findById实现如下:

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
31
32
@Override
public User findById(final String id) {
final String _sql = "SELECT * FROM user WHERE id = ?";
//通过sql创建SQLite查询执行程序
final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
int _argIndex = 1;
if (id == null) {
_statement.bindNull(_argIndex);
} else {
_statement.bindString(_argIndex, id);//绑定参数id
}
final Cursor _cursor = DBUtil.query(__db, _statement, false);//执行查询语句
//下面通过cursor获取数据并赋值给User
try {
final int _cursorIndexOfFirstName = CursorUtil.getColumnIndexOrThrow(_cursor, "first_name");
final int _cursorIndexOfName = CursorUtil.getColumnIndexOrThrow(_cursor, "name");
final int _cursorIndexOfId = CursorUtil.getColumnIndexOrThrow(_cursor, "id");
final User _result;
if(_cursor.moveToFirst()) {
_result = new User();
_result.firstName = _cursor.getString(_cursorIndexOfFirstName);
_result.name = _cursor.getString(_cursorIndexOfName);
_result.id = _cursor.getInt(_cursorIndexOfId);
} else {
_result = null;
}
return _result;
} finally {
_cursor.close();
_statement.release();
}
}

总结

  • Room对安卓原有的数据类相关类进行一次封装,对SQLiteOpenHelper, SQLiteDatabase, SQLiteProgram,SQLiteStatement,SQLiteQurey抽象出了对应的接口SupportSQLiteOpenHelper, SupportSQLiteDatabase, SupportSQLiteProgram, SupportSQLiteStatement, SupportSQLiteQurey。然后提供了一套Framework字母开头的系列类是对Support接口的实现。
  • 抽象出一系列Support接口,可以给开发者去实现一套不是基于sqlite数据库。例如可以替代sqlite的开源库realm
  • Room是一个基于DAO架构数据库框架,利用apt编译时自动生成代码来实现大量模块代码
Loading comments box needs to over the wall