一、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);
}
}