本文涉及Library的版本如下:
- androidx.room:room-runtime:2.1.0-alpha03
- androidx.room:room-compiler:2.1.0-alpha03(注解编译器)
回顾一下安卓的SQLiteOpenHelper相关类
首先放一个关于安卓数据库的类图:
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的类图结构
上图有一些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方法的实现:
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_ImplT db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);//init方法实现在RoomDatabasedb.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方法
@Overrideprotected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {//_openCallback是局部匿名内部实例, 是一个RoomOpenHelper实例, 先不管这个实例,接着看下面。final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(1) {@Overridepublic void createAllTables(SupportSQLiteDatabase _db) {//执行建表语句_db.execSQL("CREATE TABLE IF NOT EXISTS `User` (`first_name` TEXT, `name` TEXT, `id` INTEGER NOT NULL, PRIMARY KEY(`id`))");}@Overrideprotected void onCreate(SupportSQLiteDatabase _db) {if (mCallbacks != null) {for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {mCallbacks.get(_i).onCreate(_db);}}}@Overridepublic 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 {@Overridepublic SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {//new 一个FrameworkSQLiteOpenHelper, 接着看FrameworkSQLiteOpenHelper的构造器//获取SupportSQLiteOpenHelper.Configuration的context, name, callbackreturn 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/78359448private boolean mMigrated;OpenHelper(Context context, String name, final FrameworkSQLiteDatabase[] dbRef,final Callback callback) {super(context, name, null, callback.version,new DatabaseErrorHandler() {@Overridepublic void onCorruption(SQLiteDatabase dbObj) {FrameworkSQLiteDatabase db = dbRef[0];if (db != null) {callback.onCorruption(db);}}});mCallback = callback;mDbRef = dbRef;}@Overridepublic void onCreate(SQLiteDatabase sqLiteDatabase) {//数据库初始化, mCallback是RoomOpenHelpermCallback.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接口里定义方法以及注解即可。
@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()
@Overridepublic UserDao userDao() {if (_userDao != null) {return _userDao;} else {synchronized(this) {if(_userDao == null) {_userDao = new UserDao_Impl(this);}return _userDao;}}}
复制代码
接口UserDao_Impl的实现
//UserDao_Impl构造器
public UserDao_Impl(RoomDatabase __db) {this.__db = __db;// 创建一个EntityInsertionAdapter对象this.__insertionAdapterOfUser = new EntityInsertionAdapter<User>(__db) {//插入的数据的sql语句@Overridepublic String createQuery() {return "INSERT OR ABORT INTO `User`(`first_name`,`name`,`id`) VALUES (?,?,?)";}//绑定插入的字段,对应上面sql语句的‘?’占位符@Overridepublic 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);}};
} @Overridepublic 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方法如下:
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注解,大致代码如下:
@Dao
public interface UserDao {@Query("SELECT * FROM user WHERE id = :id")User findById(String id);
}
复制代码
UserDao_Impl.findById实现如下:
@Overridepublic 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获取数据并赋值给Usertry {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编译时自动生成代码来实现大量模块代码