本文涉及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編譯時自動生成代碼來實現大量模塊代碼