概述
Android 在應用框架層爲開發者提供了 SQLite 相關操作接口,其歸屬於android.database.sqlite
包底下,主要包含SQLiteProgram
, SQLiteDatabase
, SQLiteSession
, SQLiteConnectionPool
和SQLiteConnection
等一些類。相比較 SQLite 提供的輕量級接口,應用框架層爲開發者封裝的 SQLite 抽象層顯得更爲複雜,但也爲開發者屏蔽了更多細節,減小 SQLite 使用難度。
在設計上,一個 SQLiteDatabase 持有一個 SQLiteConnectionPool, SQLiteConnectionPool 包含 n 個 SQLiteConnection,其根據日誌模式的不同,連接池容量也不同。每個線程對於 SQLiteDatabase 的操作通過ThreadLocal
創建的 SQLiteSession 來進行管理,而 SQLiteSession 進行操作時需要預先獲得一個 SQLiteConnection。如果此時數據庫連接池中的連接都被使用,那麼會阻塞直到獲得可用連接。
SQLiteDatabase
SQLiteDatabase 提供了一系列管理數據庫的方法,通過它我們可以進行增刪改查和執行 SQLite 命令語句等操作。其高度封裝了SQLiteSession
, SQLiteConnectionPool
和SQLiteConnection
的執行細節,開發者僅需關心上層 API 的使用。
SQLiteDatabase 的打開
SQLiteDatabase 提供了一系列open*
函數,而這些函數的功能是打開數據庫連接,區別在於傳入的參數不同。
public static SQLiteDatabase openDatabase(@NonNull String path, @NonNull OpenParams openParams);
public static SQLiteDatabase openDatabase(@NonNull String path, @Nullable CursorFactory factory, @DatabaseOpenFlags int flags);
public static SQLiteDatabase openDatabase(@NonNull String path, @Nullable CursorFactory factory, @DatabaseOpenFlags int flags, @Nullable DatabaseErrorHandler errorHandler);
public static SQLiteDatabase openOrCreateDatabase(@NonNull String path, @Nullable CursorFactory factory);
public static SQLiteDatabase openOrCreateDatabase(@NonNull String path, @Nullable CursorFactory factory, @Nullable DatabaseErrorHandler errorHandler);
對於數據庫創建、版本升級等管理行爲則被封裝在SQLiteOpenHelper
類中。SQLiteOpenHelper
採用延遲初始化的方式來創建或打開數據庫,開發者調用getReadableDatabase
或getWritableDatabase
取得一個SQLiteDatabase
實例,而最終都會走到getDatabaseLocked
方法。
private SQLiteDatabase getDatabaseLocked(boolean writable) {
if (mDatabase != null) {
// 數據庫被關閉,此時將 SQLiteDatabase 對象置空,重新打開數據庫
if (!mDatabase.isOpen()) {
// Darn! The user closed the database by calling mDatabase.close().
mDatabase = null;
} else if (!writable || !mDatabase.isReadOnly()) {
// writable 爲 false 說明請求只讀數據庫,滿足要求
// 數據庫非只讀,支持可讀可寫,滿足要求
return mDatabase;
}
}
// 防止重複初始化
if (mIsInitializing) {
throw new IllegalStateException("getDatabase called recursively");
}
SQLiteDatabase db = mDatabase;
try {
mIsInitializing = true;
if (db != null) {
// 要求可寫,但此時數據庫以只讀方式打開,需要重新以讀寫方式打開
if (writable && db.isReadOnly()) {
db.reopenReadWrite();
}
} 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);
// Keep pre-O-MR1 behavior by resetting file permissions to 660
setFilePermissionsForDb(filePath.getPath());
// SQLiteDatabase.OpenParams 沒有配置 openFlags, 默認以讀寫方式打開數據庫
} catch (SQLException ex) {
// 打開數據庫拋出異常,如果要求以可寫方式打開,則拋出異常,否則嘗試用只讀方式打開數據庫
if (writable) {
throw ex;
}
Log.e(TAG, "Couldn't open " + mName
+ " for writing (will try read-only):", ex);
params = params.toBuilder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
db = SQLiteDatabase.openDatabase(filePath, params);
}
}
// onConfigure 回調,配置數據庫相關設置
onConfigure(db);
// 獲取當前數據庫版本
final int version = db.getVersion();
// mNewVersion 爲自行設置版本,兩者不相等時,說明首次創建數據庫或者升級或者降級數據庫(一般是更改表結構,升級數據庫)
if (version != mNewVersion) {
if (db.isReadOnly()) {
throw new SQLiteException("Can't upgrade read-only database from version " +
db.getVersion() + " to " + mNewVersion + ": " + mName);
}
// 數據庫版本小於最小支持版本,刪庫重建
if (version > 0 && version < mMinimumSupportedVersion) {
File databaseFile = new File(db.getPath());
onBeforeDelete(db);
db.close();
if (SQLiteDatabase.deleteDatabase(databaseFile)) {
mIsInitializing = false;
return getDatabaseLocked(writable);
} else {
throw new IllegalStateException("Unable to delete obsolete database "
+ mName + " with version " + version);
}
} else {
// 開啓事務,創建db或升降級
db.beginTransaction();
try {
// 第一次創建數據庫 version 默認爲0
if (version == 0) {
onCreate(db);
} else {
if (version > mNewVersion) {
onDowngrade(db, version, mNewVersion);
} else {
onUpgrade(db, version, mNewVersion);
}
}
db.setVersion(mNewVersion);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
}
// onOpen函數被回調
onOpen(db);
if (db.isReadOnly()) {
Log.w(TAG, "Opened " + mName + " in read-only mode");
}
mDatabase = db;
return db;
} finally {
mIsInitializing = false;
if (db != null && db != mDatabase) {
db.close();
}
}
}
數據庫打開相關配置參數設置:
private OpenParams(int openFlags, CursorFactory cursorFactory,
DatabaseErrorHandler errorHandler, int lookasideSlotSize, int lookasideSlotCount,
long idleConnectionTimeout, String journalMode, String syncMode) {
// 打開模式
mOpenFlags = openFlags;
mCursorFactory = cursorFactory;
// 錯誤處理器,在拋出 SQLiteDatabaseCorruptionException 時被使用
mErrorHandler = errorHandler;
// 默認120kb
mLookasideSlotSize = lookasideSlotSize;
// 默認100
mLookasideSlotCount = lookasideSlotCount;
// 數據庫空閒連接超時時間
mIdleConnectionTimeout = idleConnectionTimeout;
// 日誌模式
mJournalMode = journalMode;
// 同步模式
mSyncMode = syncMode;
}
SQLiteOpenHelper 調用的SQLiteDatabase#Open*
函數最終會打開一個數據庫主連接,這個主連接可用來讀寫,在 SQLiteConnectionPool 部分會進行詳細的介紹。
private void open() {
try {
try {
openInner();
} catch (RuntimeException ex) {
if (SQLiteDatabaseCorruptException.isCorruptException(ex)) {
Log.e(TAG, "Database corruption detected in open()", ex);
onCorruption();
openInner();
} else {
throw ex;
}
}
} 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");
}
synchronized (sActiveDatabases) {
sActiveDatabases.put(this, null);
}
}
SQLiteClosable
SQLiteClosable
是 Android 應用框架層專門爲數據庫資源釋放設計的類,類似 Java 爲 I/O 提供的Closeable
標準接口。 在android.database.sqlite
包底下的類有着大量應用,確保資源能夠及時釋放。
分析源碼,其主要提供了兩個接口:acquireReference
(獲取引用計數)和releaseReference
(釋放引用計數)(注意:這兩個的調用總是成對出現)。當引用計數爲0時,釋放所有相關資源連接,而當引用計數已經爲0,如果再次獲取引用計數,會拋出IllegalStateException
異常。
protected abstract void onAllReferencesReleased();
public void acquireReference() {
synchronized(this) {
if (mReferenceCount <= 0) {
throw new IllegalStateException(
"attempt to re-open an already-closed object: " + this);
}
mReferenceCount++;
}
}
public void releaseReference() {
boolean refCountIsZero = false;
synchronized(this) {
refCountIsZero = --mReferenceCount == 0;
}
if (refCountIsZero) {
onAllReferencesReleased();
}
}
// TODO 分析 IllegalStateException 異常出現的原因
SQLiteSession
SQLiteDatabase 的每一個操作都需要通過 SQLiteSession 來完成。每個線程對於一個數據庫最多持有一個 SQLiteSession,技術上通過ThreadLocal
來保證。這個限制確保一個線程對於給定的數據庫同一時刻不能使用多個數據庫連接,保證了單進程內數據庫使用不會產生死鎖。關於事務管理,SQLiteSession 提供 SQLite 三種事務模式的支持,並且支持事務的嵌套。在分析 SQLiteSession 發揮的作用時,我們需要事先了解一下 SQLite 中的鎖和事務機制。
SQLite的鎖機制
SQLite採用粗放型的鎖。當一個連接要寫數據庫,所有其它的連接都會被鎖住,直到寫連接結束了它的事務。SQLite 有一個加鎖表,來幫助不同的寫數據庫都能夠在最後一刻再加鎖,以保證最大的併發性。SQLite 使用鎖逐步上升機制,爲了寫數據庫,連接需要逐級地獲得排它鎖。SQLite 有5個不同的鎖狀態,每個數據庫連接在同一時刻只能處於其中一個狀態。
鎖狀態 | 鎖描述 |
---|---|
未加鎖-UNLOCKED | 數據庫此時不被讀取和寫入,其他線程或進程可以在它們的鎖狀態允許的情況下讀取或寫入數據庫。 |
共享鎖-SHARED | 數據庫可以被讀取但不能被寫入。同一時刻可以有多個線程或進程獲得 SHARED 鎖,因此同時可以有多個讀者。當有一個或多個共享鎖時,不允許其他線程或進程寫入數據庫文件。 |
保留鎖-RESERVED | RESERVED 鎖意味着進程準備寫入數據庫文件,但它目前只是從文件中讀取。一次只能有一個 RESERVED 鎖,多個 SHARED 鎖可以與一個 RESERVED 鎖共存。 RESERVED 與 PENDING 的不同之處在於,當存在 RESERVED 鎖時,可以獲取新的 SHARED 鎖。 |
未覺鎖-PENDING | PENDING 鎖意味着持有鎖的進程想要儘快寫入數據庫,並且只是等待釋放當前所有的 SHARED 鎖,以便它可以獲得 EXCLUSIVE 鎖。如果 PENDING 鎖處於活動狀態,則不允許獲得新 SHARED 鎖,但允許繼續使用現有的 SHARED 鎖。 |
排它鎖-EXCLUSIVE | 寫入數據庫時需要持有 EXCLUSIVE 鎖,同一時刻只允許只有一個 EXCLUSIVE 鎖,並且不允許其他鎖與它共存。爲了最大限度地提高併發性,SQLite 應儘量減少持有獨佔鎖的時間。 |
SQLite的事務類型
SQLite 支持多個數據庫連接同時發起讀事務,但寫事務同時只能有一個。讀事務僅用於讀取,寫事務則允許讀取和寫入。讀事務由 SELECT 語句啓動,寫入事務由 CREATE、DELETE、DROP、INSERT 或 UPDATE 等語句啓動(統稱爲“寫入語句”)。SQLite 提供了三種不同的事務類型,它們以不同的鎖狀態啓動事務類型,這三種事務類型爲:DEFERRED、IMMEDIATE、EXCLUSIVE,默認的事務類型是DEFERRED。事務在 BEGIN 類型中指定:
BEGIN [ DEFERRED | IMMEDIATE | EXCLUSIVE ] TRANSACTION;
BEGIN DEFERRED
開啓的事務不獲取任何鎖,直到它需要鎖的時候。BEGIN 語句本身不會做什麼事情,它開始於 UNLOCK 狀態。默認情況下如果僅僅用 BEGIN 開始一個事務,那麼事務就是 DEFERRED 的,同時它不會獲取任何鎖。當對數據庫進行第一次讀操作時,它會獲取 SHARED 鎖。同樣,當進行第一次寫操作時,它會獲取 RESERVED 鎖。
BEGIN IMMEDIATE
開啓的事務會嘗試獲取 RESERVED 鎖。如果成功,BEGIN IMMEDIATE
保證沒有別的連接可以寫數據庫。但是別的連接可以對數據庫進行讀操作。但是 RESERVED 鎖會阻止其它連接的BEGIN IMMEDIATE
或者BEGIN EXCLUSIVE
命令。當其它連接執行上述命令時,會返回SQLITE_BUSY
錯誤。當執行 COMMIT 操作時,如果返回SQLITE_BUSY
錯誤,這意味着還有其它的讀事務沒有完成,得等它們執行完後才能提交事務。
BEGIN EXCLUSIVE
開啓的事務會試着獲取對數據庫的 EXCLUSIVE 鎖。這與BEGIN IMMEDIATE
類似,但是一旦成功,EXCLUSIVE 事務保證沒有其它的連接,所以就可對數據庫進行讀寫操作了。EXCLUSIVE 和 IMMEDIATE 在 WAL 模式下是一樣的,但在其他日誌模式下,EXCLUSIVE 會阻止其他數據庫連接在事務進行時讀取數據庫。
事務的開啓與結束
上面兩個小節介紹了 SQLite 中的鎖機制和事務相關概念,現在來看一下 Android 應用框架層是如何進行封裝的。
SQLiteDatabase 提供了幾個開啓事務的方法,這幾個方法主要區別在於傳入的參數不同,最終實際調用public void beginTransaction(SQLiteTransactionListener transactionListener, boolean exclusive)
方法。在這個方法中尤爲重要的是exclusive
參數,其決定了開啓的事務類型,當爲 true 時調用BEGIN EXCLUSIVE
; 否則執行BEGIN IMMEDIATE
。
public void beginTransaction(); // 實際調用 beginTransaction(null, true)
public void beginTransactionNonExclusive(); // 實際調用 beginTransaction(null, false)
public void beginTransactionWithListener(SQLiteTransactionListener transactionListener); // 實際調用 beginTransaction(transactionListener, true)
public void beginTransactionWithListenerNonExclusive(SQLiteTransactionListener transactionListener)); // 實際調用 beginTransaction(transactionListener, false)
public void beginTransaction(SQLiteTransactionListener transactionListener, boolean exclusive);
SQLiteDatabase 的一系列開啓事務方法最終走到SQLiteSession#beginTransactionUnchecked
。通過棧的數據結構保存嵌套事務的關係,嵌套事務的執行都在同一個數據庫連接當中。如果任何嵌套事務執行失敗,那麼當最外層事務結束時,包括其所有嵌套事務在內的整個事務將被回滾。
private void beginTransactionUnchecked(int transactionMode,
SQLiteTransactionListener transactionListener, int connectionFlags,
CancellationSignal cancellationSignal) {
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
}
// 首次執行事務需要先獲得一個數據庫連接,當有嵌套事務時,最頂層事務獲得一次數據庫連接,嵌套的事務都使用這個連接對數據庫進行操作
if (mTransactionStack == null) {
acquireConnection(null, connectionFlags, cancellationSignal); // might throw
}
try {
// transactionMode在上層調用 SQLiteDatabase#beginTransaction(SQLiteTransactionListener transactionListener, boolean exclusive)時確定,
// transactionMode = exclusive ? SQLiteSession.TRANSACTION_MODE_EXCLUSIVE :SQLiteSession.TRANSACTION_MODE_IMMEDIATE
if (mTransactionStack == null) {
// Execute SQL might throw a runtime exception.
switch (transactionMode) {
case TRANSACTION_MODE_IMMEDIATE:
mConnection.execute("BEGIN IMMEDIATE;", null,
cancellationSignal); // might throw
break;
case TRANSACTION_MODE_EXCLUSIVE:
mConnection.execute("BEGIN EXCLUSIVE;", null,
cancellationSignal); // might throw
break;
default:
mConnection.execute("BEGIN;", null, cancellationSignal); // might throw
break;
}
}
// Listener might throw a runtime exception.
if (transactionListener != null) {
try {
transactionListener.onBegin(); // might throw
} catch (RuntimeException ex) {
if (mTransactionStack == null) {
mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
}
throw ex;
}
}
// 從對象池中複用對象,對象池爲鏈表,每次從隊尾取得無用的對象
Transaction transaction = obtainTransaction(transactionMode, transactionListener);
// 通過棧的方式存儲事務,通過這種形式來對嵌套事務進行支持,棧頂爲最內層的事務
transaction.mParent = mTransactionStack;
mTransactionStack = transaction;
} finally {
if (mTransactionStack == null) {
releaseConnection(); // might throw
}
}
}
private void endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding) {
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
}
// 獲取棧頂的事務
final Transaction top = mTransactionStack;
// 事務被標記成功或暫時讓步且其下級事務沒有失敗時認爲成功,yielding 爲true的case 爲 yieldTransaction 的調用
boolean successful = (top.mMarkedSuccessful || yielding) && !top.mChildFailed;
RuntimeException listenerException = null;
final SQLiteTransactionListener listener = top.mListener;
if (listener != null) {
try {
if (successful) {
listener.onCommit(); // might throw
} else {
listener.onRollback(); // might throw
}
} catch (RuntimeException ex) {
listenerException = ex;
successful = false;
}
}
// 當前棧頂的上一級事務
mTransactionStack = top.mParent;
// 將無用的事務對象緩存進對象池中
recycleTransaction(top);
// 判斷事務棧中所有的事務是否執行完成
if (mTransactionStack != null) {
if (!successful) {
// 標記下級事務失敗
mTransactionStack.mChildFailed = true;
}
} else {
// 已經是棧底事務,整個事務只要有一個失敗,那麼會導致整個事務全部失敗
try {
// 提交本次事務的修改
if (successful) {
mConnection.execute("COMMIT;", null, cancellationSignal); // might throw
} else {
// 回滾本次事務的修改
mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
}
} finally {
releaseConnection(); // might throw
}
}
if (listenerException != null) {
throw listenerException;
}
}
現在我們來看一下beginTransactionUnchecked
方法中connectionFlags
的確定。由SQLiteDatabase#beginTransaction(SQLiteTransactionListener transactionListener, boolean exclusive)
的調用會走入下面的代碼,connectionFlags
會被標記爲需要獲取主連接。因此,顯式的在代碼裏面調用beginTransaction
方法,即使執行的是讀語句也會獲取主連接,而同時只能獲取一個主連接,因此會導致性能下降。所以如果讀語句沒有必要,無需顯式調用事務開啓方法。
@UnsupportedAppUsage
private void beginTransaction(SQLiteTransactionListener transactionListener,
boolean exclusive) {
acquireReference();
try {
getThreadSession().beginTransaction(
exclusive ? SQLiteSession.TRANSACTION_MODE_EXCLUSIVE :
SQLiteSession.TRANSACTION_MODE_IMMEDIATE,
transactionListener,
getThreadDefaultConnectionFlags(false /*readOnly*/), null);
} finally {
releaseReference();
}
}
int getThreadDefaultConnectionFlags(boolean readOnly) {
int flags = readOnly ? SQLiteConnectionPool.CONNECTION_FLAG_READ_ONLY :
SQLiteConnectionPool.CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY;
if (isMainThread()) {
flags |= SQLiteConnectionPool.CONNECTION_FLAG_INTERACTIVE;
}
return flags;
}
SQLiteConnectionPool
SQLiteConnectionPool 主要用於緩存數據庫連接,其包含一個主連接(擁有讀寫能力)和若干非主連接(擁有隻讀能力)。
連接池的創建
// 在構造器中確定連接池的大小
private SQLiteConnectionPool(SQLiteDatabaseConfiguration configuration) {
mConfiguration = new SQLiteDatabaseConfiguration(configuration);
setMaxConnectionPoolSizeLocked();
// If timeout is set, setup idle connection handler
// In case of MAX_VALUE - idle connections are never closed
if (mConfiguration.idleConnectionTimeoutMs != Long.MAX_VALUE) {
setupIdleConnectionHandler(Looper.getMainLooper(),
mConfiguration.idleConnectionTimeoutMs);
}
}
// 對於非內存數據庫且開啓了 WAL 日誌模式的數據庫,其池大小由內建的配置決定,一般爲4個,取決於不同機型內部的設置,包含1個主連接和 poolSize - 1的非主連接。
// 通過源碼也可以看出,連接池大小最小爲2
private void setMaxConnectionPoolSizeLocked() {
if (!mConfiguration.isInMemoryDb()
&& (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
mMaxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();
} else {
// We don't actually need to always restrict the connection pool size to 1
// for non-WAL databases. There might be reasons to use connection pooling
// with other journal modes. However, we should always keep pool size of 1 for in-memory
// databases since every :memory: db is separate from another.
// For now, enabling connection pooling and using WAL are the same thing in the API.
mMaxConnectionPoolSize = 1;
}
}
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);
}
// 連接池中主要打開一個數據庫主連接
private void open() {
// Open the primary connection.
// This might throw if the database is corrupt.
mAvailablePrimaryConnection = openConnectionLocked(mConfiguration,
true /*primaryConnection*/); // might throw
// Mark it released so it can be closed after idle timeout
synchronized (mLock) {
if (mIdleConnectionHandler != null) {
mIdleConnectionHandler.connectionReleased(mAvailablePrimaryConnection);
}
}
// Mark the pool as being open for business.
mIsOpen = true;
mCloseGuard.open("close");
}
獲取連接
SQLiteSession 對數據庫進行操作前需要獲得一個數據庫連接,其最終調用SQLiteConnectionPool#acquireConnection
來取得一個數據庫連接。
// SQLiteDatabase#getThreadDefaultConnectionFlags
int getThreadDefaultConnectionFlags(boolean readOnly) {
int flags = readOnly ? SQLiteConnectionPool.CONNECTION_FLAG_READ_ONLY :
SQLiteConnectionPool.CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY;
if (isMainThread()) {
flags |= SQLiteConnectionPool.CONNECTION_FLAG_INTERACTIVE;
}
return flags;
}
// connectionFlags 表示連接類型,其通過 SQLiteDatabase#getThreadDefaultConnectionFlags 確定
public SQLiteConnection acquireConnection(String sql, int connectionFlags,
CancellationSignal cancellationSignal) {
SQLiteConnection con = waitForConnection(sql, connectionFlags, cancellationSignal);
synchronized (mLock) {
if (mIdleConnectionHandler != null) {
mIdleConnectionHandler.connectionAcquired(con);
}
}
return con;
}
// 等待可用的連接
private SQLiteConnection waitForConnection(String sql, int connectionFlags,
CancellationSignal cancellationSignal) {
// 通過 connectionFlags 確定是否是主連接,當需要寫能力時 wantPrimaryConnection 爲true
final boolean wantPrimaryConnection =
(connectionFlags & CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY) != 0;
final ConnectionWaiter waiter;
final int nonce;
synchronized (mLock) {
throwIfClosedLocked();
// Abort if canceled.
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
}
// Try to acquire a connection.
SQLiteConnection connection = null;
// 需要非主連接,嘗試從可用連接池中獲取一個連接,如果沒有達到池上限,創建一個數據庫連接
if (!wantPrimaryConnection) {
connection = tryAcquireNonPrimaryConnectionLocked(
sql, connectionFlags); // might throw
}
// 沒有可用的非主連接或需要主連接,嘗試獲取主連接
if (connection == null) {
connection = tryAcquirePrimaryConnectionLocked(connectionFlags); // might throw
}
if (connection != null) {
return connection;
}
// 沒有可用的主連接,根據連接優先級插入隊列,如果是在主線程獲取連接,則爲高優先級
final int priority = getPriority(connectionFlags);
final long startTime = SystemClock.uptimeMillis();
waiter = obtainConnectionWaiterLocked(Thread.currentThread(), startTime,
priority, wantPrimaryConnection, sql, connectionFlags);
ConnectionWaiter predecessor = null;
ConnectionWaiter successor = mConnectionWaiterQueue;
// 維護了一個等待鏈表,從鏈表頭開始查找插入位置
while (successor != null) {
if (priority > successor.mPriority) {
waiter.mNext = successor;
break;
}
predecessor = successor;
successor = successor.mNext;
}
if (predecessor != null) {
predecessor.mNext = waiter;
} else {
mConnectionWaiterQueue = waiter;
}
nonce = waiter.mNonce;
}
// Set up the cancellation listener.
if (cancellationSignal != null) {
cancellationSignal.setOnCancelListener(new CancellationSignal.OnCancelListener() {
@Override
public void onCancel() {
synchronized (mLock) {
if (waiter.mNonce == nonce) {
cancelConnectionWaiterLocked(waiter);
}
}
}
});
}
try {
// 默認30s
long busyTimeoutMillis = CONNECTION_POOL_BUSY_MILLIS;
long nextBusyTimeoutTime = waiter.mStartTime + busyTimeoutMillis;
for (;;) {
// 檢測處於泄漏的連接並使用它
if (mConnectionLeaked.compareAndSet(true, false)) {
synchronized (mLock) {
// 喚醒等待
wakeConnectionWaitersLocked();
}
}
// 使當前線程進入休眠,最長30s
LockSupport.parkNanos(this, busyTimeoutMillis * 1000000L);
// Clear the interrupted flag, just in case.
Thread.interrupted();
// Check whether we are done waiting yet.
synchronized (mLock) {
throwIfClosedLocked();
final SQLiteConnection connection = waiter.mAssignedConnection;
final RuntimeException ex = waiter.mException;
// 釋放的可用連接是否給到當前這個等待的 waiter,如果是則直接返回
if (connection != null || ex != null) {
recycleConnectionWaiterLocked(waiter);
if (connection != null) {
return connection;
}
throw ex; // rethrow!
}
// 沒有獲得可用連接,從新調整休眠時間
final long now = SystemClock.uptimeMillis();
if (now < nextBusyTimeoutTime) {
busyTimeoutMillis = now - nextBusyTimeoutTime;
} else {
logConnectionPoolBusyLocked(now - waiter.mStartTime, connectionFlags);
busyTimeoutMillis = CONNECTION_POOL_BUSY_MILLIS;
nextBusyTimeoutTime = now + busyTimeoutMillis;
}
}
}
} finally {
// Remove the cancellation listener.
if (cancellationSignal != null) {
cancellationSignal.setOnCancelListener(null);
}
}
}
釋放連接
public void releaseConnection(SQLiteConnection connection) {
synchronized (mLock) {
if (mIdleConnectionHandler != null) {
mIdleConnectionHandler.connectionReleased(connection);
}
AcquiredConnectionStatus status = mAcquiredConnections.remove(connection);
if (status == null) {
throw new IllegalStateException("Cannot perform this operation "
+ "because the specified connection was not acquired "
+ "from this pool or has already been released.");
}
// 數據庫已經關閉,直接關閉連接
if (!mIsOpen) {
closeConnectionAndLogExceptionsLocked(connection);
} else if (connection.isPrimaryConnection()) {
// 主連接不是要關閉,則賦值
if (recycleConnectionLocked(connection, status)) {
assert mAvailablePrimaryConnection == null;
mAvailablePrimaryConnection = connection;
}
// 喚醒等待連接的線程
wakeConnectionWaitersLocked();
// 非主連接超過池子規定的大小,關閉
} else if (mAvailableNonPrimaryConnections.size() >= mMaxConnectionPoolSize - 1) {
closeConnectionAndLogExceptionsLocked(connection);
} else {
if (recycleConnectionLocked(connection, status)) {
mAvailableNonPrimaryConnections.add(connection);
}
// 喚醒等待連接的線程
wakeConnectionWaitersLocked();
}
}
}