Sqlite 源碼分析 -- 獲取數(shù)據(jù)庫、創(chuàng)建連接池、建立主連接 (API 24)

一、SQLiteOpenHelper 調(diào)用入口

/**
 * Open the database according to the flags {@link #OPEN_READWRITE}
 * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}.
 *
 * <p>Sets the locale of the database to the  the system's current locale.
 * Call {@link #setLocale} if you would like something else.</p>
 *
 * <p>Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
 * used to handle corruption when sqlite reports database corruption.</p>
 *
 * @param path to database file to open and/or create
 * @param factory an optional factory class that is called to instantiate a
 *            cursor when query is called, or null for default
 * @param flags to control database access mode
 * @param errorHandler the {@link DatabaseErrorHandler} obj to be used to handle corruption
 * when sqlite reports database corruption
 * @return the newly opened database
 * @throws SQLiteException if the database cannot be opened
 */
public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags, DatabaseErrorHandler errorHandler) {
    // 創(chuàng)建 SQLiteDatabase 對象
    SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler);
    // 創(chuàng)建連接池,打開主連接
    db.open();
    return db;
}

1. SQLiteDatabase 構(gòu)造方法,創(chuàng)建 SQLiteDatabase 對象

private SQLiteDatabase(String path, int openFlags, CursorFactory cursorFactory, DatabaseErrorHandler errorHandler) {
    // mCursorFactory 默認(rèn)為 null
    mCursorFactory = cursorFactory;
    // 創(chuàng)建 onCorruption 的回調(diào)處理對象,DefaultDatabaseErrorHandler 的處理是刪除數(shù)據(jù)庫文件
    // 可以通過 SQLiteOpenHelper 的構(gòu)造方法傳入,用于自定義數(shù)據(jù)庫損壞時的操作
    mErrorHandler = errorHandler != null ? errorHandler : new DefaultDatabaseErrorHandler();
    mConfigurationLocked = new SQLiteDatabaseConfiguration(path, openFlags);
}

2. db.open() 初始化連接池

private void open() {
    try {
        try {
            // 創(chuàng)建連接池,打開主連接
            openInner();
        } catch (SQLiteDatabaseCorruptException ex) {
            // 默認(rèn)處理是,若數(shù)據(jù)庫已關(guān)閉(連接池賦值為 null),則刪除數(shù)據(jù)庫文件
            onCorruption();
            // 再次嘗試
            openInner();
        }
    } catch (SQLiteException ex) {
        Log.e(TAG, "Failed to open database '" + getLabel() + "'.", ex);
        // 如果連接池不為 null,則關(guān)閉連接池
        close();
        throw ex;
    }
}

private void openInner() {
    synchronized (mLock) {
        // assert 斷言,為 true 時則繼續(xù)執(zhí)行
        assert mConnectionPoolLocked == null;
        // 創(chuàng)建連接池,打開主連接
        mConnectionPoolLocked = SQLiteConnectionPool.open(mConfigurationLocked);
        mCloseGuardLocked.open("close");
    }

    synchronized (sActiveDatabases) {
        sActiveDatabases.put(this, null);
    }
}

二、初始化 SQLiteConnectionPool

1. SQLiteConnectionPool.open() 調(diào)用入口

/**
 * 創(chuàng)建連接池,打開主連接
 * Opens a connection pool for the specified database.
 *
 * @param configuration The database configuration.
 * @return The connection pool.
 *
 * @throws SQLiteException if a database error occurs.
 */
public static SQLiteConnectionPool open(SQLiteDatabaseConfiguration configuration) {
    if (configuration == null) {
        throw new IllegalArgumentException("configuration must not be null.");
    }

    // Create the pool.
    SQLiteConnectionPool pool = new SQLiteConnectionPool(configuration);
    // 打開主連接
    pool.open(); // might throw
    return pool;
}

2. SQLiteConnectionPool 構(gòu)造方法,創(chuàng)建連接池對象

private SQLiteConnectionPool(SQLiteDatabaseConfiguration configuration) {
    // 保存數(shù)據(jù)庫配置項
    mConfiguration = new SQLiteDatabaseConfiguration(configuration);
    // 設(shè)置連接池最大連接數(shù)
    // 1. 默認(rèn)情況下,連接數(shù)量為 1
    // 2. 開啟多線程并發(fā)功能后,連接數(shù)量由系統(tǒng)配置,最少有兩條
    setMaxConnectionPoolSizeLocked();
}

/**
 * 設(shè)置連接池最大連接數(shù)
 * 1. 默認(rèn)情況下,連接數(shù)量為 1
 * 2. 開啟多線程并發(fā)功能后,連接數(shù)量由系統(tǒng)配置,最少有兩條
 */
private void setMaxConnectionPoolSizeLocked() {
    if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
        // 開啟了多線程并發(fā)功能,該功能默認(rèn)不開啟
        // 連接數(shù)量由系統(tǒng)配置,最少有兩條
        mMaxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();
    } else {
        // TODO: We don't actually need to restrict the connection pool size to 1
        // for non-WAL databases.  There might be reasons to use connection pooling
        // with other journal modes.  For now, enabling connection pooling and
        // using WAL are the same thing in the API.
        // 注意此處,連接數(shù)量默認(rèn)為 1
        mMaxConnectionPoolSize = 1;
    }
}

/**
 * 返回值 >= 2
 * Gets the connection pool size when in WAL mode.
 */
public static int getWALConnectionPoolSize() {
    int value = SystemProperties.getInt("debug.sqlite.wal.poolsize",
            Resources.getSystem().getInteger(com.android.internal.R.integer.db_connection_pool_size));
    return Math.max(2, value);
}

三、 打開連接池主連接

/**
 * 打開主連接
 * Might throw
 */
private void open() {
    // Open the primary connection.
    // This might throw if the database is corrupt.
    // 打開主連接
    mAvailablePrimaryConnection = openConnectionLocked(mConfiguration, true /*primaryConnection*/); // might throw

    // Mark the pool as being open for business.
    mIsOpen = true;
    mCloseGuard.open("close");
}

// Might throw.
private SQLiteConnection openConnectionLocked(SQLiteDatabaseConfiguration configuration, boolean primaryConnection) {
    final int connectionId = mNextConnectionId++;
    return SQLiteConnection.open(this, configuration, connectionId, primaryConnection); // might throw
}

1. 創(chuàng)建連接(主連接)

/**
 * 1. 創(chuàng)建連接
 * 2. 打開連接
 *
 * @param pool
 * @param configuration
 * @param connectionId
 * @param primaryConnection 是否是主連接
 * @return
 */
// Called by SQLiteConnectionPool only.
static SQLiteConnection open(SQLiteConnectionPool pool, SQLiteDatabaseConfiguration configuration, int connectionId, boolean primaryConnection) {
    // 創(chuàng)建連接
    SQLiteConnection connection = new SQLiteConnection(pool, configuration, connectionId, primaryConnection);
    try {
        // 打開連接
        connection.open();
        return connection;
    } catch (SQLiteException ex) {
        connection.dispose(false);
        throw ex;
    }
}

2. SQLiteConnection 構(gòu)造方法

private SQLiteConnection(SQLiteConnectionPool pool, SQLiteDatabaseConfiguration configuration, int connectionId, boolean primaryConnection) {
    mPool = pool;
    mConfiguration = new SQLiteDatabaseConfiguration(configuration);
    mConnectionId = connectionId;
    // 是否為主連接
    mIsPrimaryConnection = primaryConnection;
    // 是否是只讀連接
    mIsReadOnlyConnection = (configuration.openFlags & SQLiteDatabase.OPEN_READONLY) != 0;
    mPreparedStatementCache = new SQLiteConnection.PreparedStatementCache(mConfiguration.maxSqlCacheSize);
    mCloseGuard.open("close");
}

3. 打開連接

private void open() {
    // 調(diào)用 native 方法打開連接
    mConnectionPtr = nativeOpen(mConfiguration.path, mConfiguration.openFlags, mConfiguration.label, SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME);
    // 如果是只讀連接或者數(shù)據(jù)庫只存在于內(nèi)存中,則不做任何處理
    setPageSize();
    // 如果是只讀連接則不做任何處理
    setForeignKeyModeFromConfiguration();
    // 如果是只讀連接或者數(shù)據(jù)庫只存在于內(nèi)存中,則不做任何處理
    setWalModeFromConfiguration();
    // 如果是只讀連接或者數(shù)據(jù)庫只存在于內(nèi)存中,則不做任何處理
    setJournalSizeLimit();
    // 如果是只讀連接或者數(shù)據(jù)庫只存在于內(nèi)存中,則不做任何處理
    setAutoCheckpointInterval();
    setLocaleFromConfiguration();

    // Register custom functions.
    final int functionCount = mConfiguration.customFunctions.size();
    for (int i = 0; i < functionCount; i++) {
        SQLiteCustomFunction function = mConfiguration.customFunctions.get(i);
        nativeRegisterCustomFunction(mConnectionPtr, function);
    }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 從三月份找實習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍(lán)閱讀 42,795評論 11 349
  • 注意事項: 如果 SQLiteOpenHelper 使用的是單例,SQLiteDatabase 對 CRUD 操作...
    _夜閱讀 623評論 0 0
  • 想吃些甜點嗎?在芬蘭人的食品中,乳制品扮演了非常重要的角色。 如果去芬蘭超市乳制品貨架區(qū)走一圈,你會一下子感到很驚...
    慕溪北歐旅游閱讀 1,125評論 0 0
  • 1 全世界已知的癌癥大概有260種,直男癌是第261種。 直男癌的定義是活在自己的世界里,時時向別人流露出不順眼及...
    林陌鹿閱讀 883評論 2 8
  • 找個相愛的人牽手到老,如果愛情耗盡了,耐性磨完了,請用責(zé)任喚醒曾經(jīng)那個你愛的自己! 我不知道未來會怎么樣,可是...
    果丹皮_e2f6閱讀 297評論 0 0

友情鏈接更多精彩內(nèi)容