Android 中的数据库 SQLite

SQLite 的简介

Sqlite数据库是一种轻量级数据库,它具备跨平台,多语言操作等优点,它广泛用于包括浏览器、IOS,Android以及一些便携需求的小型web应用系统。它具备占用资源低,处理速度快等优点。

Android 中操作 SQLite 的方式

  1. SQLiteOpenHelper 和 SQLiteDatabase,Android 内部封装的用于管理数据库创建和版本管理的帮助类。
  2. GreenDao,一款轻量级的 ORM 数据库框架,可以通过注解对象映射到 SQLite 数据库中。
  3. Room,Google 官方推荐的使用的操作 SQLite 的方式,也是一个 ORM 数据库框架,也是通过注解对象映射到 SQLite 数据库中。
  4. LitePal,通过 XML 文件对象映射到 SQLite 数据库中。

SQLiteOpenHelper 和 SQLiteDatabase

SQLiteOpenHelper 是 Android 对于管理 SQLite 数据库创建、打开、关闭、升级、降级以及一些操作回调的帮助类。

创建和打开 SQLite 数据库

明确:分析创建和打开数据库是分析打开数据库的连接

注意:创建 SQLiteOpenHelper 对象并未进行创建或打开 SQLite 数据库,需要调用 getWritableDatabase 或 getReadableDatabase 方法才可创建或打开数据库。

//@Link #SQLiteOpenHelper
// 创建或打开一个可用于读、写操作的数据库
public SQLiteDatabase getWritableDatabase() {
    synchronized (this) {//同步锁
        return getDatabaseLocked(true);
    }
}
// 创建或打开一个可用于读、写操作的数据库
public SQLiteDatabase getReadableDatabase() {
    synchronized (this) {
        return getDatabaseLocked(false);
    }
}
private SQLiteDatabase getDatabaseLocked(boolean writable) {
    if (mDatabase != null) {
        if (!mDatabase.isOpen()) {
            // 数据库关闭需要重新打开
            mDatabase = null;
        } else if (!writable || !mDatabase.isReadOnly()) {
            // 不需要写入数据库,当前数据库也是仅读数据库,则可以返回
            return mDatabase;
        }
    }
	...
    SQLiteDatabase db = mDatabase;
    try {
        mIsInitializing = true;
        if (db != null) {
            // 若需要写数据库,当前仅可读,需要重新打开数据库
            if (writable && db.isReadOnly()) {
                db.reopenReadWrite();
            }
            // 数据库名字为 null,则再内存中创建一个数据库,数据库关闭,内容消失。
        } else if (mName == null) {
            db = SQLiteDatabase.createInMemory(mOpenParamsBuilder.build());
        } else {
            final File filePath = mContext.getDatabasePath(mName);
            SQLiteDatabase.OpenParams params = mOpenParamsBuilder.build();
            try {
                // 在此进行打开数据库操作
                db = SQLiteDatabase.openDatabase(filePath, params);
                setFilePermissionsForDb(filePath.getPath());
            } catch (SQLException ex) {
                if (writable) {
                    throw ex;
                }
                //.... 出现异常,则尝试打开可写数据库
                params = params.toBuilder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
                db = SQLiteDatabase.openDatabase(filePath, params);
            }
        }
		// 数据库配置完成回调
        onConfigure(db);

        final int version = db.getVersion();
        if (version != mNewVersion) {
           //...在此进行对数据库的版本处理
           //可能会进行 onBeforeDelete 删除之前、onCreate 创建、onDowngrade 降级、onUpgrade 升级回调
        }
		// 数据库打开回调
        onOpen(db);
        ...
        mDatabase = db;
        return db;
    } finally {
        mIsInitializing = false;
        if (db != null && db != mDatabase) {
            db.close();
        }
    }
}

在上面的代码中,数据库名字 null 时通过 SQLiteDatabase.createInMemory(mOpenParamsBuilder.build()); 在内存中创建一个临时数据库,否则通过 SQLiteDatabase.openDatabase(filePath, params); 打开或创建一个指定路径、名字的数据库,下面主要分析打开数据库操作。

//@Link #SQLiteDatabase
public static SQLiteDatabase openDatabase(@NonNull File path,
                                          @NonNull OpenParams openParams) {
    return openDatabase(path.getPath(), openParams);
}
private static SQLiteDatabase openDatabase(@NonNull String path,
                                           @NonNull OpenParams openParams) {
    Preconditions.checkArgument(openParams != null, "OpenParams cannot be null");
    // SQLiteDatabase 的构造函数中进行数据库的参数配置
    SQLiteDatabase db = new SQLiteDatabase(path, openParams.mOpenFlags,
                                           openParams.mCursorFactory, openParams.mErrorHandler,
                                           openParams.mLookasideSlotSize, openParams.mLookasideSlotCount,
                                           openParams.mIdleConnectionTimeout, openParams.mJournalMode, openParams.mSyncMode);
    db.open(); //打开数据库
    return db;
}

上面的方法都是一些配置信息,最终会调用 db.open() 进行真正的数据库打开操作,进行创建数据库连接池,以及创建初步的数据库连接(SQLiteConnection)。

//@Link #SQLiteDatabase
private void open() {
    try {
        try {
            openInner();
        } catch (SQLiteDatabaseCorruptException ex) {
            onCorruption();
            openInner();
        }
    } catch (SQLiteException ex) {
        Log.e(TAG, "Failed to open database '" + getLabel() + "'.", ex);
        close();
        throw ex;
    }
}
private void openInner() {
    synchronized (mLock) {
        assert mConnectionPoolLocked == null;
        // 初始化数据库连接池
        mConnectionPoolLocked = SQLiteConnectionPool.open(mConfigurationLocked);
        mCloseGuardLocked.open("close");
    }
	//...
}

//@Link #SQLiteConnectionPool
 public static SQLiteConnectionPool open(SQLiteDatabaseConfiguration configuration) {
     //...
     SQLiteConnectionPool pool = new SQLiteConnectionPool(configuration);
     pool.open();//打开数据库连接池
     return pool;
 }
private void open() {
    // 创建主要数据库连接,通过 SQLiteConnection 可以通过 SQL 语句对数据库进行操作(类似 JDBC)
    mAvailablePrimaryConnection = openConnectionLocked(mConfiguration,
                                                       true /*primaryConnection*/); 
    synchronized (mLock) {
        if (mIdleConnectionHandler != null) {
            mIdleConnectionHandler.connectionReleased(mAvailablePrimaryConnection);
        }
    }
	//...
}
private SQLiteConnection openConnectionLocked(SQLiteDatabaseConfiguration configuration,
                                                  boolean primaryConnection) {
    final int connectionId = mNextConnectionId++;
    // 在此打开数据库连接,并返回 SQLiteConnection
    return SQLiteConnection.open(this, configuration,
                                     connectionId, primaryConnection); // might throw
}

通过上面代码分析,可以知道 SQLiteDatabase 数据库维护一个 SQLiteConnectionPool 数据库连接池,在任何时候,数据库连接 SQLiteConnection 都属于数据库连接池。数据库连接池是线程安全的,内部大多数方法都通过 synchronized (mLock) 来实现加锁,但是数据库连接不是线程安全的。

总结一下:SQLiteDatabase 是对数据库连接池和数据库连接的维护以及对操作数据库的方法的封装,

SQLiteOpenHelper 是一个用于管理数据库创建、关闭、升级、降级的管理类。最终的数据库操作都是通过 SQLiteConnection 调用数据库 native 层方法来进行。打开数据库相当于创建数据库连接池和创建数据库连接

通过 SQLiteDatabase 操作数据库

明确:操作数据库是分析如何通过 SQLiteDatabase 获取 SQLiteConnection,并通过数据库连接调用 native 的过程.

SQLiteDatabase 内封装了对数据库操作的方法,可以通过封装的方法,无需进行书写 SQL 语句进行操作数据库,不仅如此,SQLiteDatabase 还支持执行原生的 SQL 语句。

SQLiteDatabase 封装操作数据库的方法是通过传入的参数进行拼接成 SQL 语句,再进行操作数据库。选取 Insert 插入操作进行分析,其他操作数据库的方法思想大同小异。

//@Link #SQLiteDatabase
//插入操作 table:表名,nullColumnHack :处理插入空行, values :字段名和值
public long insert(String table, String nullColumnHack, ContentValues values) {
    try {
        return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
    } catch (SQLException e) {
        Log.e(TAG, "Error inserting " + values, e);
        return -1;
    }
}
public long insertWithOnConflict(String table, String nullColumnHack,
                                 ContentValues initialValues, int conflictAlgorithm) {
    acquireReference();
    try {
        StringBuilder sql = new StringBuilder();
        // 拼接 SQL 语句的类型
        sql.append("INSERT");
        sql.append(CONFLICT_VALUES[conflictAlgorithm]);
        sql.append(" INTO ");
        sql.append(table);
        sql.append('(');
        Object[] bindArgs = null;
        int size = (initialValues != null && !initialValues.isEmpty())
            ? initialValues.size() : 0;
        if (size > 0) {
            bindArgs = new Object[size];
            int i = 0;
            // 拼接字段名字
            for (String colName : initialValues.keySet()) {
                sql.append((i > 0) ? "," : "");
                sql.append(colName);
                bindArgs[i++] = initialValues.get(colName);
            }
            sql.append(')');
            sql.append(" VALUES (");
            // 拼接字段对应的值
            for (i = 0; i < size; i++) {
                sql.append((i > 0) ? ",?" : "?");
            }
        } else {
            sql.append(nullColumnHack + ") VALUES (NULL");
        }
        sql.append(')');
		// 通过 SQL 语句创建 SQLiteStatement 对象
        SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
        try {
            // 执行插入操作
            return statement.executeInsert();
        } finally {
            statement.close();
        }
    } finally {
        releaseReference();
    }
}

其实在上面已经构建好一个 SQL 语句,以及通过 SQL 语句构建了一个 SQLiteStatement 对象,(SQLiteStatement 是对 SQLiteSession 的一层封,SQLiteSession 用于处理执行 SQL 语句的细节,如:事务、异常等。SQLiteStatement 则是用于调用 SQLiteSession 的方法,及调用方法过程中出现异常的回调,如:onCorruption())装,那么接下来,需要去挖究竟是如何到 SQLConnection 执行这条 SQL 语句。

//@Link #SQLiteStatement
public long executeInsert() {
    acquireReference();
    try {
        // getSession() 返回的是 SQLiteSession 对象,可以理解为相 SQLiteDatabase 控制 SQLiteConnection 的类。
        return getSession().executeForLastInsertedRowId(
            getSql(), getBindArgs(), getConnectionFlags(), null);
    } catch (SQLiteDatabaseCorruptException ex) {
        onCorruption();
        throw ex;
    } finally {
        releaseReference();
    }
}

//@Link #SQLiteSession
public long executeForLastInsertedRowId(String sql, Object[] bindArgs, int connectionFlags,
                                        CancellationSignal cancellationSignal) {
    //...在这里向 SQLiteConectionPool 数据库连接池请求获取 SQLiteConnection 数据库连接
    acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
    try {
        // 在这里就到了通过 SQLiteConnection 数据库连接进行执行上面构建好了的 SQL 语句
        return mConnection.executeForLastInsertedRowId(sql, bindArgs,
                                                       cancellationSignal); // might throw
    } finally {
        releaseConnection(); // might throw
    }
}

在这不进行对在数据库连接池获取 SQLiteConnection 数据库连接的过程进行分析。

总结一下:在获取到 SQLiteDatabase 后进行数据库操作时,填入的参数首先会进行构建成 SQL 语句,通过 SQL 语句构建成 SQLiteStatement,再获取到 SQLiteSession,通过 SQLiteSession 获取到数据库连接池中的 SQLiteConnection 执行最初构建的 SQL 语句。

SQLiteDatabase 还支持直接执行原生的 SQL 语句(除了 Query 查询操作的 SQL 语句),到 SQLiteStatement 之后的分析与上面的 Insert 插入操作的分析类同,不予再分析。

//@Link #SQLiteDatabase
public void execSQL(String sql) throws SQLException {
    executeSql(sql, null);
}
private int executeSql(String sql, Object[] bindArgs) throws SQLException {
    acquireReference();
    try {
        // DatabaseUtils.getSqlStatementType(sql) 是获取当前 SQL 语句的操作类型
        final int statementType = DatabaseUtils.getSqlStatementType(sql);
        // DatabaseUtils.STATEMENT_ATTACH 是可以用来 创建或者附加数据库数据库
        if (statementType == DatabaseUtils.STATEMENT_ATTACH) {
          	//... 一些配置操作
        }
		// 创建 SQLiteStatement 对象
        try (SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs)) {
            // 通过 executeUpdateDelete 执行 SQL 语句
            // 从这可以看出 SQLiteDatabase 不支持通过原生 SQL 进行查询操作。
            return statement.executeUpdateDelete();
        } finally {
            if (statementType == DatabaseUtils.STATEMENT_DDL) {
                mConnectionPoolLocked.closeAvailableNonPrimaryConnectionsAndLogExceptions();
            }
        }
    } finally {
        releaseReference();
    }
}

Room

Room是一个数据库对象映射库,可以轻松访问Android应用程序上的数据库。Room 提供方便的 API 来查询数据库,并在编译的时候验证这些查询。

Room 有 3 个主要的组件

  1. @Database:该注解是作用于一个继承了 RoomDatabase 数据库的抽象类(abstract)类,在运行期间,可以通过 Room.databaseBuilder 或 Room.inMemoryDatabaseBuilder 获取 RoonmDatabase 实例。该数据库类定义了数据库的实体(Entity)表和数据库访问对象(Dao),也是底层连接的主要访问点。
  2. @Entity:该注解作用于一个实体类,实体类对应与数据库中一个表,实体类的各个成员属性对应于数据库表中的字段。数据库对于每一个用 @Entity 注解了的类都创建一个对应的表进行存储。
  3. @Dao:该注解作用于一个类或者一个接口,作为访问数据库的对象。访问数据库的对象是 Room 的主要组件,负责定义访问数据库的方法。在被 @Database 作用的类中,必须有一个无参返回 @Dao 作用类对象的抽象方法。在编译期间会通过注解处理器自动生成被 @Dao 作用的类的具体实现。

在编译期间,Room 的注解处理器会自动帮我们生成访问数据库的类(Dao 类)和生成创建数据库的类(Database 类)。通过 Room 注解处理器生成的类,也相当于普通的写代码,只不过该代码是通过 JDK 自动生成,JDK 搜索所有注解处理器,注解处理器识别注解,反射获取注解作用的类或方法获取到信息参数,再根据参数生成相应的类对应的逻辑。

注解处理器有点像模板功能,我们输入点内容,然后注解处理器通过我们输入的内容生成模板代码,避免在开发过程中反复写这些模板代码,让开发者更加关注业务的逻辑实现。

Room 初始化 @Database

明确:框架都是对底层东西进行的封装,对数据库的封装自然是对 SQLiteDatabase 和 SQLiteOpenHelper 的封装,所以先挖一下是在创建 SQLiteDatabase 实例和是哪个类操作 SQLiteDatabase。

下面是开发过程中自己实现的 AppDatabase 类,通过该类可以获取到 RoomDatabase 数据库的实例。

//@Link #AppDatabase
@Database(entities = {ArticleEntity.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
    private static final String DB_NAME = "WanAndroid.db";
    private static volatile AppDatabase sAppDatabase;
    // 通常一个数据库的访问入口在 App 运行期间只有一个实例,所以使用单例模式。
    public static synchronized AppDatabase getInstance(Context context) {
        if (sAppDatabase == null) {
            synchronized (AppDatabase.class) {
                if (sAppDatabase == null) {
                    sAppDatabase = create(context);
                }
            }
        }
        return sAppDatabase;
    }
    private static AppDatabase create(final Context context) {
        // 创建 RoomDatabase
        return Room.databaseBuilder(
                context,
                AppDatabase.class,
                DB_NAME).build();
    }
	// 对应上面说的,在被 @Database 作用的类中,必须有一个无参返回 @Dao 作用类对象的抽象方法
    public abstract ArticleDao getArticleDao();
}

创建 RoomDatabase 实例是通过 Builder 模式创建的,接着来看一下 build() 方法的代码。

//@Link #RoomDatabase.Builder
public T build() {
    //...进行一些判断,以及查询线程池和事务线程池(Executor)的创建或初始化
    
    //...

    if (mFactory == null) {
        // 构建 FrameworkSQLiteOpenHelperFactory 实例
        mFactory = new FrameworkSQLiteOpenHelperFactory();
    }
        DatabaseConfiguration configuration =
            new DatabaseConfiguration(
            mContext,
            mName,
            mFactory,
            mMigrationContainer,
            mCallbacks,
            mAllowMainThreadQueries,
            mJournalMode.resolve(mContext),
            mQueryExecutor,
            mTransactionExecutor,
            mMultiInstanceInvalidation,
            mRequireMigration,
            mAllowDestructiveMigrationOnDowngrade,
            mMigrationsNotRequiredFrom);
        T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);// 通过反射获取到 mDatabaseClass 子类的实例(例子中子类为:AppDatabase_Impl, mDatabaseClass 是 AppDatabase 类)
        db.init(configuration); // 配置 mDatabaseClass 子类实例
        return db;
	}
}

//@Link #FrameworkSQLiteOpenHelperFactory
// FrameworkSQLiteOpenHelperFactory 是用于构建 FrameworkSQLiteOpenHelper 的工厂类
public final class FrameworkSQLiteOpenHelperFactory implements SupportSQLiteOpenHelper.Factory {
    // 所有相关的类对应的对象都要等工厂类调用 create() 方法才被构建。
    @Override
    public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {
        return new FrameworkSQLiteOpenHelper(
                configuration.context, configuration.name, configuration.callback);
    }
}

//@Link #FrameworkSQLiteOpenHelper
// FrameworkSQLiteOpenHelper 代理了它的内部类 OpenHelper,OpenHelper(继承了 SQLiteOpenHelper) 维护 FrameworkSQLiteDatabase 数据库的创建以及一些回调
FrameworkSQLiteOpenHelper(Context context, String name, Callback callback) {
    mDelegate = createDelegate(context, name, callback);// 创建代理 OpenHelper 实例
}
private OpenHelper createDelegate(Context context, String name, Callback callback) {
    // FrameworkSQLiteDatabase 是 SQLiteDatabase 的代理类
    // 创建 FrameworkSQLiteDatabase 数组,只是存在引用,未构建实例
    final FrameworkSQLiteDatabase[] dbRef = new FrameworkSQLiteDatabase[1];
    return new OpenHelper(context, name, dbRef, callback);
}

//@Link #FrameworkSQLiteOpenHelper.OpenHelper
// OpenHelper 被 FrameworkSQLiteOpenHelper 类代理,是 FrameworkSQLiteOpenHelper 的内部类
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) {
                  // 虽然 getWrappedDb(dbRef, dbObj) 是实例化 FrameworkSQLiteDatabase 的实例
                  // 但是在此回调未实例化代理 SQLiteDatabase 的 FrameworkSQLiteDatabase 的实例
                  callback.onCorruption(getWrappedDb(dbRef, dbObj));
              }
          });
    mCallback = callback;
    mDbRef = dbRef;
}

//@Link #FrameworkSQLiteOpenHelper.OpenHelper
// 此方法是真正实例化代理了 SQLiteDatabase 的 FrameworkSQLiteDatabase 对象
static FrameworkSQLiteDatabase getWrappedDb(FrameworkSQLiteDatabase[] refHolder,
                                            SQLiteDatabase sqLiteDatabase) {
    FrameworkSQLiteDatabase dbRef = refHolder[0];
    if (dbRef == null || !dbRef.isDelegate(sqLiteDatabase)) {
        
        refHolder[0] = new FrameworkSQLiteDatabase(sqLiteDatabase);
    }
    return refHolder[0];
}

通过上面的分析,可以得到 Room 都是通过代理模式来对原生的 SQLiteOpenHelper 和 SQLiteDatabase 进行封装。上面提到,所有相关的类对应的对象都要等 FrameworkSQLiteOpenHelperFactory 调用 create() 方法才被构建,但是上面并未进行调用 create() 方法,而是将所有的 Room 配置信息都存到了 DatabaseConfiguration 中,那么接着来看 Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX); 的方法调用。

//@Link #Room
@SuppressWarnings({"TypeParameterUnusedInFormals", "ClassNewInstance"})
@NonNull
static <T, C> T getGeneratedImplementation(Class<C> klass, String suffix) {
    //... 获取 klass 类的信息
    // 这是获取 klass 对应的子类包名+类名,(如例子中的 klass 类名 = AppDatabase,implName =  AppDatabase_Impl)
    final String implName = postPackageName.replace('.', '_') + suffix;
    try {

        @SuppressWarnings("unchecked")
        final Class<T> aClass = (Class<T>) Class.forName(
            fullPackage.isEmpty() ? implName : fullPackage + "." + implName);
        // 在此构建 AppDatabase 的子类 AppDatabase_Impl 的实例
        return aClass.newInstance();
    }
    //... 异常处理
}

上面提到 AppDatabase_Impl,这是 @Database 注解对应的注解处理器生成的类(生成路基:{project_root}\app\build\generated\source\apt\debug\{包名}\db\AppDatabase_Impl.java)。在获取到 AppDatabase_Impl 实例后,进行调用 db.init(configuration); 进行配置数据库。接下来要分析配置数据库的过程,不出意外,之前说的 ’FrameworkSQLiteOpenHelperFactory 调用 create() 方法‘ 将会在配置数据库的时候调用。

//@Link #RoomDatabase
@CallSuper
public void init(@NonNull DatabaseConfiguration configuration) {
    // 在此创建 SupportSQLiteOpenHelper(FrameworkSQLiteOpenHelper 是具体实现,代理了 OpenHelper)
    // createOpenHelper 是抽象方法,具体实现在其子类(例子中为 AppDatabase_Impl)
    mOpenHelper = createOpenHelper(configuration);
    // 下面都是一些 RoomDatabase 的成员变量的赋值操作
    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;
    mQueryExecutor = configuration.queryExecutor;
    mTransactionExecutor = new TransactionExecutor(configuration.transactionExecutor);
    mAllowMainThreadQueries = configuration.allowMainThreadQueries;
    mWriteAheadLoggingEnabled = wal;
    if (configuration.multiInstanceInvalidation) {
        mInvalidationTracker.startMultiInstanceInvalidation(configuration.context,
                                                            configuration.name);
    }
}

//@Link #AppDatabase_Impl
@Override
protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
    final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate(1) {
        @Override
        public void createAllTables(SupportSQLiteDatabase _db) {
            //... 创建所有的表
        }
		//...数据库操作的回调处理
    }, "3b4f5822228d6c99c8dc8fa0e6468f7f", "4953443b8f2c213519c76f3b51546987");
    final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
        .name(configuration.name)
        .callback(_openCallback)
        .build();
    // create() 方法在此调用,进行实例化代理 SQLiteOpenHelper 实例
    // Room 到此就算初始化完成
    final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
    return _helper;
}

**总结一下:**通过上面的代码可以发现,Room 的实现,是通过代理来进行对原生 SQLiteDatabase 和原生 SQLiteOpenHelper 进行封装,并且通过注解处理器(AnnotationProcessor)进行生成 Database 和 Dao 的代码。

到现在,Room 已经初始化完成,但是还没创建原生 SQLiteDatabase 的实例和代理 SQLiteDatabase 的 FrameworkSQLiteDatabase 实例还没创建,仅存在它的引用,上面提到真正实例化 FrameworkSQLiteDatabase 的方法是 getWrappedDb(FrameworkSQLiteDatabase[] refHolder, SQLiteDatabase sqLiteDatabase)。那么可以初步猜测是在进行数据库操作的时候,会进行调用 getWrappedDb 的方法。

Room 操作数据库 @Dao

选取通过 Room 插入数据来分析 Room 操作数据库。

被 @Dao 注解作用的接口主要是定义了一些进行操作的方法,@Dao 作用接口配合 @Insert 作用方法的使用,可以进行插入数据进入数据库,通过 RoomDatabase 可以获取 Dao 实例进行操作数据库。

//@Link #ArticleDao
@Dao
public interface ArticleDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insert(ArticleEntity articleEntities);
}
// 使用
AppDatabase.getInstance(App.getContext()).getArticleDao().insert(entities);

//@Link #AppDatabase_Impl
//getArticleDao() 是获取到 ArticleDao_Impl 的实例(单例模式)
public ArticleDao getArticleDao() {
    if (_articleDao != null) {
      return _articleDao;
    } else {
      synchronized(this) {
        if(_articleDao == null) {
          _articleDao = new ArticleDao_Impl(this);
        }
        return _articleDao;
      }
    }
  }

上面提到 ArticleDao_Impl 类,这个类是我们写的 ArticleDao 接口的具体实现,当然这个类也是 @Dao 注解的注解处理器进行生成的代码,在获取到 ArticleDao 实例后,可以进行执行 ArticleDao 里面定义的操作数据库的方法。下面来看看通过 ArticleDao 来进行插入操作。

//@Link #ArticleDao_Impl
@Override
public void insert(final ArticleEntity... articleEntities) {
    __db.assertNotSuspendingTransaction();// 确认没有暂停的事务
    // 开始事务,在这里面会进行 SQLiteDatabase (通过代理类 FrameworkSQLiteOpenHelper.OpenHelper 打开)的打开
    __db.beginTransaction();
    try {
        __insertionAdapterOfArticleEntity.insert(articleEntities);// 插入操作
        __db.setTransactionSuccessful();// 设置事务成功
    } finally {
        __db.endTransaction();// 结束事务
    }
}

//@Link #RoomDatabase
@Deprecated
public void beginTransaction() {
    assertNotMainThread();// 确认不是在主线程
    SupportSQLiteDatabase database = mOpenHelper.getWritableDatabase();
    mInvalidationTracker.syncTriggers(database);
    database.beginTransaction();//开始事务
}

//@Link #FrameworkSQLiteOpenHelper
// mDelegate 的引用类型是 FrameworkSQLiteOpenHelper.OpenHelper
@Override
public SupportSQLiteDatabase getWritableDatabase() {
    return mDelegate.getWritableSupportDatabase();
}

//@Link #FrameworkSQLiteOpenHelper.OpenHelper
synchronized SupportSQLiteDatabase getWritableSupportDatabase() {
    mMigrated = false;
    // 在此也进行打开 SQLiteDatabase 数据库的操作(就是原生数据库打开的方式)
    SQLiteDatabase db = super.getWritableDatabase();
    if (mMigrated) {
        close();
        return getWritableSupportDatabase();
    }
    return getWrappedDb(db);
}
FrameworkSQLiteDatabase getWrappedDb(SQLiteDatabase sqLiteDatabase) {
     // 在这就对应了上面所说的调用 getWrappedDb(mDbRef, sqLiteDatabase) 进行实例化 FrameworkSQLiteDatabase 实例代理 SQLiteDatabase
    return getWrappedDb(mDbRef, sqLiteDatabase);
}

总结一下:在 Room 初始化的时候仅实例化了代理 SQLiteOpenHelper 的 FrameworkSQLiteOpenHelper.OpenHelper,当通过 Room 操作数据库的时候,才会打开 SQLiteDatabse 数据库并将代理 SQLiteDatabase 的 FrameworkSQLiteDatabase 实例化。

接着看通过 Room 进行插入操作 __insertionAdapterOfArticleEntity.insert(articleEntities);。EntityInsertionAdapter 是用于让 Room 知道如何去插入一个实体的类,比如插入一条数据库记录可以通过该实例进行将实体的各个属性进行绑定到 SQL 语句中。

//@Link #EntityInsertionAdapter 
public final void insert(T entity) {
    final SupportSQLiteStatement stmt = acquire();// 底层通过 RoomDatabase 获取数据库声明(SQLiteStatement)
    try {
        // 实体和 SQLiteStatement 进行绑定(通俗说就是将实体的内容装到 SQL 语句的占位符中)
        bind(stmt, entity); 
        // 在这就进行执行插入操作了
        stmt.executeInsert();
    } finally {
        release(stmt);
    }
}

到这就到了上面分析的原生数据库的操作方式了,就不继续向下分析,详情往上看。

Room 操作的实体 @Entity

通过 @Entity 注解的类对应的是数据库中的一张表,类中的成员变量是表中的各个字段名,该类也是 Room 操作数据库的对象。(注意:必须有 set 和 get 方法)

Room 主要是通过代理来实现封装底层 SQLiteOpenHelper 和 SQLiteDatabase ,以及通过注解处理器的方式来进行生成模板代码,简化开发的代码量,让开发者更快速地操作数据库。

GreenDao

GreenDao 和 Room 差不多,也是将 JAVA 对象映射到 SQLite 数据库中。GreenDao 是一款开源的 Android ORM 数据库框架,具有API 简单,易于使用,支持加密,框架小的特点。

GreenDao 是通过 FreeMarker 生成模板代码。Room 是通过注解处理器生成模板代码。FreeMarker 暂未研究。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值