Sqlite 源码分析 -- 获取数据库、创建连接池、建立主连接 (API 24)

一、SQLiteOpenHelper 调用入口

/**
 * 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) {
    // 创建 SQLiteDatabase 对象
    SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler);
    // 创建连接池,打开主连接
    db.open();
    return db;
}

1. SQLiteDatabase 构造方法,创建 SQLiteDatabase 对象

private SQLiteDatabase(String path, int openFlags, CursorFactory cursorFactory, DatabaseErrorHandler errorHandler) {
    // mCursorFactory 默认为 null
    mCursorFactory = cursorFactory;
    // 创建 onCorruption 的回调处理对象,DefaultDatabaseErrorHandler 的处理是删除数据库文件
    // 可以通过 SQLiteOpenHelper 的构造方法传入,用于自定义数据库损坏时的操作
    mErrorHandler = errorHandler != null ? errorHandler : new DefaultDatabaseErrorHandler();
    mConfigurationLocked = new SQLiteDatabaseConfiguration(path, openFlags);
}

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

private void open() {
    try {
        try {
            // 创建连接池,打开主连接
            openInner();
        } catch (SQLiteDatabaseCorruptException ex) {
            // 默认处理是,若数据库已关闭(连接池赋值为 null),则删除数据库文件
            onCorruption();
            // 再次尝试
            openInner();
        }
    } catch (SQLiteException ex) {
        Log.e(TAG, "Failed to open database '" + getLabel() + "'.", ex);
        // 如果连接池不为 null,则关闭连接池
        close();
        throw ex;
    }
}

private void openInner() {
    synchronized (mLock) {
        // assert 断言,为 true 时则继续执行
        assert mConnectionPoolLocked == null;
        // 创建连接池,打开主连接
        mConnectionPoolLocked = SQLiteConnectionPool.open(mConfigurationLocked);
        mCloseGuardLocked.open("close");
    }

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

二、初始化 SQLiteConnectionPool

1. SQLiteConnectionPool.open() 调用入口

/**
 * 创建连接池,打开主连接
 * 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 构造方法,创建连接池对象

private SQLiteConnectionPool(SQLiteDatabaseConfiguration configuration) {
    // 保存数据库配置项
    mConfiguration = new SQLiteDatabaseConfiguration(configuration);
    // 设置连接池最大连接数
    // 1. 默认情况下,连接数量为 1
    // 2. 开启多线程并发功能后,连接数量由系统配置,最少有两条
    setMaxConnectionPoolSizeLocked();
}

/**
 * 设置连接池最大连接数
 * 1. 默认情况下,连接数量为 1
 * 2. 开启多线程并发功能后,连接数量由系统配置,最少有两条
 */
private void setMaxConnectionPoolSizeLocked() {
    if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
        // 开启了多线程并发功能,该功能默认不开启
        // 连接数量由系统配置,最少有两条
        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.
        // 注意此处,连接数量默认为 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. 创建连接(主连接)

/**
 * 1. 创建连接
 * 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) {
    // 创建连接
    SQLiteConnection connection = new SQLiteConnection(pool, configuration, connectionId, primaryConnection);
    try {
        // 打开连接
        connection.open();
        return connection;
    } catch (SQLiteException ex) {
        connection.dispose(false);
        throw ex;
    }
}

2. SQLiteConnection 构造方法

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() {
    // 调用 native 方法打开连接
    mConnectionPtr = nativeOpen(mConfiguration.path, mConfiguration.openFlags, mConfiguration.label, SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME);
    // 如果是只读连接或者数据库只存在于内存中,则不做任何处理
    setPageSize();
    // 如果是只读连接则不做任何处理
    setForeignKeyModeFromConfiguration();
    // 如果是只读连接或者数据库只存在于内存中,则不做任何处理
    setWalModeFromConfiguration();
    // 如果是只读连接或者数据库只存在于内存中,则不做任何处理
    setJournalSizeLimit();
    // 如果是只读连接或者数据库只存在于内存中,则不做任何处理
    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);
    }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容