
Android 存儲優(yōu)化系列專題
- SharedPreferences 系列
《Android 之不要濫用 SharedPreferences》
《Android 之不要濫用 SharedPreferences(2)— 數(shù)據(jù)丟失》
- ContentProvider 系列(待更)
《Android 存儲選項之 ContentProvider 啟動過程源碼分析》
《Android 存儲選項之 ContentProvider 深入分析》
- 對象序列化系列
《Android 對象序列化之你不知道的 Serializable》
《Android 對象序列化之 Parcelable 深入分析》
《Android 對象序列化之追求完美的 Serial》
- 數(shù)據(jù)序列化系列(待更)
《Android 數(shù)據(jù)序列化之 JSON》
《Android 數(shù)據(jù)序列化之 Protocol Buffer 使用》
《Android 數(shù)據(jù)序列化之 Protocol Buffer 源碼分析》
- SQLite 存儲系列
《Android 存儲選項之 SQLiteDatabase 創(chuàng)建過程源碼分析》
《Android 存儲選項之 SQLiteDatabase 源碼分析》
《數(shù)據(jù)庫連接池 SQLiteConnectionPool 源碼分析》
《SQLiteDatabase 啟用事務源碼分析》
《SQLite 數(shù)據(jù)庫 WAL 模式工作原理簡介》
《SQLite 數(shù)據(jù)庫鎖機制與事務簡介》
《SQLite 數(shù)據(jù)庫優(yōu)化那些事兒》
在上篇《Android 存儲選項之 SQLiteDatabase 源碼分析》一文為大家介紹了數(shù)據(jù)庫操作相關源碼。先來簡單回顧下,通過使用 SQLiteDatabase 執(zhí)行 SQLite 數(shù)據(jù)庫操作,會根據(jù)當前線程創(chuàng)建一個私有的 SQLiteSession,SQLiteSession 保證了同一個句柄在同一時間僅有一個線程在操作,SQLiteSession 通過數(shù)據(jù)庫連接池 SQLiteConnectionPool 獲取一個數(shù)據(jù)庫連接 SQLiteConnection。每一個 SQLiteConnection 持有一個數(shù)據(jù)庫訪問句柄完成數(shù)據(jù)庫操作任務。
不過關于 SQLiteConnectionPool 中數(shù)據(jù)庫連接的管理機制并沒有詳細介紹,今天我們就結(jié)合源碼詳細分析下 SQLiteConnectionPool 是如何管理數(shù)據(jù)庫連接的。
SQLiteConnectionPool 主要用于緩存所有的數(shù)據(jù)庫連接,包括一個主連接和若干條非主連接。注意主連接有且僅有一個(表示可寫的),非主連接數(shù)量取決于連接池大小配置(最大值 - 1(1 表示主連接))。
連接的獲取
接下來從 SQLiteSession 開始跟蹤獲取一個數(shù)據(jù)庫連接的過程,前面也有提到 SQLiteDatabase 通過 ThreadLocal 保證線程私有的 SQLiteSession,從而保證了同一個句柄同一時間僅有一個線程操作。SQLiteSession 中獲取數(shù)據(jù)庫連接方法如下:
//在SQLiteConnectionPool中申請一個數(shù)據(jù)庫連接
private void acquireConnection(String sql, int connectionFlags,
CancellationSignal cancellationSignal) {
if (mConnection == null) {
assert mConnectionUseCount == 0;
//到連接池獲取一個數(shù)據(jù)庫連接
mConnection = mConnectionPool.acquireConnection(sql, connectionFlags,
cancellationSignal); // might throw
mConnectionFlags = connectionFlags;
}
mConnectionUseCount += 1;
}
SQLiteSession 中持有當前 SQLiteDatabase 的數(shù)據(jù)庫連接池,通過該連接池嘗試獲取一個可用的數(shù)據(jù)庫連接。
先看下 SQLiteSession 的構(gòu)造方法,如下:
public SQLiteSession(SQLiteConnectionPool connectionPool) {
if (connectionPool == null) {
throw new IllegalArgumentException("connectionPool must not be null");
}
//持有當前SQLiteDatabase的數(shù)據(jù)庫連接池
mConnectionPool = connectionPool;
}
SQLiteConnectionPool 中嘗試獲取一個數(shù)據(jù)庫連接的過程如下:
public SQLiteConnection acquireConnection(String sql, int connectionFlags,
CancellationSignal cancellationSignal) {
//獲取一個SQLiteConnection, connectionFlags如果在主線程是 0101 否則 0001
SQLiteConnection con = waitForConnection(sql, connectionFlags, cancellationSignal);
synchronized (mLock) {
if (mIdleConnectionHandler != null) {
//移除該連接的超時關閉時間
mIdleConnectionHandler.connectionAcquired(con);
}
}
return con;
}
- 參數(shù)說明
- connectionFlags
這里首先說下 connectionFlags 的取值,它表示當前獲取連接的類型,該取值最終是通過 SQLiteDatabase 的 getThreadDefaultConnectionFlags 方法完成,如下:
int getThreadDefaultConnectionFlags(boolean readOnly) {
//如果是查詢操作,此時readOnly就是CONNECTION_FLAG_READ_ONLY
//CONNECTION_FLAG_READ_ONLY == 0001
//CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY == 0010
int flags = readOnly ? SQLiteConnectionPool.CONNECTION_FLAG_READ_ONLY :
SQLiteConnectionPool.CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY;
//如果是主線程
if (isMainThread()) {
//CONNECTION_FLAG_INTERACTIVE == 0100
//此時如果是只讀計算的值為:0001 != 0100 = 0101
//否則:0010 != 0100 = 0110
flags |= SQLiteConnectionPool.CONNECTION_FLAG_INTERACTIVE;
}
return flags;
}
如果參數(shù) readOnly = true(只讀模式)返回 0001 否則 0010。
另外它還會受到當前是否是在主線程操作的影響,此時 readOnly = true 時 0001 & 0100 = 0101,否則 0010 & 0100 = 0110。
其實它的主要作用就是區(qū)分當前操作是讀還是寫數(shù)據(jù)庫操作(查詢屬于讀數(shù)據(jù)庫,插入、刪除、更新都屬于寫數(shù)據(jù)庫操作),這在嘗試獲取數(shù)據(jù)庫連接時,決定獲取主連接還是非主連接。
- mIdleConnectionHandler 作用
當數(shù)據(jù)庫連接長時間處于空閑狀態(tài)也是對資源的一種浪費,SQLiteConnectionPool 提供了數(shù)據(jù)庫連接空閑超時關閉機制,簡單點說,就是通過發(fā)送延遲消息(允許最大的空閑時間)來釋放超時的空閑連接。
//移除該連接的空閑超時關閉機制
mIdleConnectionHandler.connectionAcquired(con);
回到上面 acquireConnection 方法,調(diào)用 waitForConnection 去獲取一個數(shù)據(jù)庫連接,該方法內(nèi)容較多,主要劃分為兩部分:
(1) 嘗試立即獲取數(shù)據(jù)庫連接。
(2) 等待獲取數(shù)據(jù)庫連接。
后者是當前不存在空閑的數(shù)據(jù)庫連接,并且連接數(shù)量已經(jīng)達到允許最大值,此時需要按照優(yōu)先級排隊等待連接釋放。
- 嘗試立即獲取連接
先來看下嘗試立即獲取數(shù)據(jù)庫連接的過程,如下:
private SQLiteConnection waitForConnection(String sql, int connectionFlags,
CancellationSignal cancellationSignal) {
//查詢操作時 0001 & 0010 或者 0101(表示主線程) & 0010,此時值為false
//如果是需要寫入操作updata/insert/delete 0010 & 0010 或者 0110(表示主線程) & 0010,此時值為true
final boolean wantPrimaryConnection =
(connectionFlags & CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY) != 0;
final ConnectionWaiter waiter;
final int nonce;
synchronized (mLock) {
//如果操作已經(jīng)關閉的將會拋出異常
throwIfClosedLocked();
// Abort if canceled.
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
}
// Try to acquire a connection.
SQLiteConnection connection = null;
//如果是查詢操作此時嘗試獲取非主連接
if (!wantPrimaryConnection) {
//如果當前>=最大連接池數(shù)量則返回null
connection = tryAcquireNonPrimaryConnectionLocked(
sql, connectionFlags); // might throw
}
if (connection == null) {
//嘗試獲取主連接,如果是寫操作,默認是需要獲取主連接的
connection = tryAcquirePrimaryConnectionLocked(connectionFlags); // might throw
}
if (connection != null) {
return connection;
}
//... 等待獲取過程省略
}
根據(jù) connectionFlags 計算當前需要可讀 / 可寫的數(shù)據(jù)庫連接,只讀時首先獲取非主連接,否則嘗試獲取主連接。先看下嘗試立即獲取非主連接的過程 tryAcquireNonPrimaryConnectionLocked 方法如下:
//獲取非主連接
private SQLiteConnection tryAcquireNonPrimaryConnectionLocked(
String sql, int connectionFlags) {
// Try to acquire the next connection in the queue.
SQLiteConnection connection;
//mAvailableNonPrimaryConnections是緩存所有非主連接的List對象
final int availableCount = mAvailableNonPrimaryConnections.size();
if (availableCount > 1 && sql != null) {
// If we have a choice, then prefer a connection that has the
// prepared statement in its cache.
for (int i = 0; i < availableCount; i++) {
connection = mAvailableNonPrimaryConnections.get(i);
//優(yōu)先獲取已經(jīng)執(zhí)行過該SQL的連接,SQLiteConnection中會
//緩存編譯SQL后的PreparedStatement對象
if (connection.isPreparedStatementInCache(sql)) {
//找到在緩存中移除
mAvailableNonPrimaryConnections.remove(i);
//加入到WeakHashMap緩存
finishAcquireConnectionLocked(connection, connectionFlags); // might throw
return connection;
}
}
}
//如果上面沒有根據(jù)SQL語句優(yōu)先獲取到SQLiteConnection
//此時存在空閑連接
if (availableCount > 0) {
// Otherwise, just grab the next one.
//否則就獲取最近緩存的一個
connection = mAvailableNonPrimaryConnections.remove(availableCount - 1);
//加入到WeakHashMap緩存
finishAcquireConnectionLocked(connection, connectionFlags); // might throw
return connection;
}
// Expand the pool if needed.
//獲取當前正在執(zhí)行任務的數(shù)量
int openConnections = mAcquiredConnections.size();
if (mAvailablePrimaryConnection != null) {
//如果主連接是空閑的,此時實際連接數(shù)量要+1
openConnections += 1;
}
if (openConnections >= mMaxConnectionPoolSize) {
//如果大于允許的最大連接池大小,return null
return null;
}
//否則創(chuàng)建一個新的連接
connection = openConnectionLocked(mConfiguration,
false /*primaryConnection*/); // might throw
//加入到正在使用緩存
finishAcquireConnectionLocked(connection, connectionFlags); // might throw
return connection;
}
可以看到優(yōu)先根據(jù)要執(zhí)行的 SQL 語句獲取對應的 SQLiteConnection 對象,SQLiteConnection 中通過 LRU 默認會緩存執(zhí)行過的 SQL 語句,默認最大緩存?zhèn)€數(shù)為 25,其內(nèi)部實際緩存的是 SQL 語句編譯后的 PreparedStatement 對象。
如果不能根據(jù) SQL 語句優(yōu)先獲取到,則默認取出最后一個(availableCount - 1)返回。
如果當前無可復用的連接(availableCount <= 0),此時需要根據(jù)當前連接池允許最大數(shù)量決定是否要創(chuàng)建一個新的連接返回,否則直接返回 null。后續(xù)就要通過等待的方式獲取了。
這里的 finishAcquireConnectionLocked 方法需要說明下作用
private void finishAcquireConnectionLocked(SQLiteConnection connection, int connectionFlags) {
try {
//需要的數(shù)據(jù)庫是否是只讀的
final boolean readOnly = (connectionFlags & CONNECTION_FLAG_READ_ONLY) != 0;
//將連接設置為相應操作模式
connection.setOnlyAllowReadOnlyOperations(readOnly);
//將其緩存到WeakHashMap
mAcquiredConnections.put(connection, AcquiredConnectionStatus.NORMAL);
} catch (RuntimeException ex) {
Log.e(TAG, "Failed to prepare acquired connection for session, closing it: "
+ connection + ", connectionFlags=" + connectionFlags);
closeConnectionAndLogExceptionsLocked(connection);
throw ex; // rethrow!
}
}
mAcquiredConnections 是 WeakHashMap 容器,主要緩存當前正在執(zhí)行任務的數(shù)據(jù)庫連接。判斷當前正在執(zhí)行操作連接(SQLiteConnection)的數(shù)量。
接下來看下立即獲取主連接的過程,主連接的獲取與非主連接有一定區(qū)別,一起來看下:
//嘗試立即獲取數(shù)據(jù)庫主連接
private SQLiteConnection tryAcquirePrimaryConnectionLocked(int connectionFlags) {
//主連接是直接作為成員緩存在SQLiteConnectionPool中
SQLiteConnection connection = mAvailablePrimaryConnection;
if (connection != null) {
//將其在SQLiteConnectionPool中持有置為null,表示當前無空閑主連接
mAvailablePrimaryConnection = null;
//標志正在執(zhí)行任務的連接
finishAcquireConnectionLocked(connection, connectionFlags); // might throw
return connection;
}
// Make sure that the primary connection actually exists and has just been acquired.
for (SQLiteConnection acquiredConnection : mAcquiredConnections.keySet()) {
//遍歷標志正在執(zhí)行任務的容器,判斷如果存在主連接,表示當前有一條主連接正在工作
if (acquiredConnection.isPrimaryConnection()) {
//此時直接返回null,一個SQLiteConnectionPool僅能有一個主連接。
return null;
}
}
//走到這里說明既沒有空閑的主連接,也沒有正在執(zhí)行任務的主連接
//此時直接創(chuàng)建一個主連接。
connection = openConnectionLocked(mConfiguration,
true /*primaryConnection*/); // might throw
//加入到執(zhí)行標志
finishAcquireConnectionLocked(connection, connectionFlags); // might throw
return connection;
}
前面我們有多次提到,每一個數(shù)據(jù)庫連接池有且僅有一條數(shù)據(jù)庫主連接,主連接表示可寫的。關于這部分如果想進一步了解可以參考下一篇《Android 存儲選項之 SQLite 優(yōu)化那些事兒》會做進一步介紹。
mAvailablePrimaryConnection 表示當前數(shù)據(jù)庫主連接,它直接作為成員在 SQLiteConnectionPool 中,根據(jù)其是否為 null 判斷當前是否存在空閑的主連接,如果不存在則表示主連接正在執(zhí)行任務(也可能因為空閑超時已經(jīng)被回收),所以 for 循環(huán)就是判斷當前是否存在正在工作的主連接。
如果不存在正在執(zhí)行任務的主連接和不存在空閑的主連接(已經(jīng)被空閑超時回收),此時需要創(chuàng)建一條數(shù)據(jù)庫主連接并返回。
至此關于數(shù)據(jù)庫連接的立即獲取就已經(jīng)分析完了,整個過程相對還是比較容易理解的。不過它們的返回都有可能為 null,此時表示連接數(shù)量已達到最大值,這個時候就需用等待其它連接釋放。重新回到 waitForConnection 方法看下等待獲取連接過程。
- 等待獲取連接
private SQLiteConnection waitForConnection(String sql, int connectionFlags,
CancellationSignal cancellationSignal) {
// 立即獲取連接過程上面已經(jīng)做了分析,這里直接省略
//優(yōu)先級的獲取跟當前操作是否在主線程有關
//如果是在主線程返回1,否則 0
final int priority = getPriority(connectionFlags);
//當前時間
final long startTime = SystemClock.uptimeMillis();
//waiter是一個ConnectionWaiter,是一個單向鏈表結(jié)構(gòu),存在一個同類的mNext成員
//obtainConnectionWaiterLocked會去復用(取鏈表頭)或者新建一個
waiter = obtainConnectionWaiterLocked(Thread.currentThread(), startTime,
priority, wantPrimaryConnection, sql, connectionFlags);
ConnectionWaiter predecessor = null;
//當前等待獲取數(shù)據(jù)庫連接池隊列
ConnectionWaiter successor = mConnectionWaiterQueue;
while (successor != null) {
//按照順序調(diào)整當前等待隊列
if (priority > successor.mPriority) {
//如果當前優(yōu)先級 > mConnectionWaiterQueue的優(yōu)先級
waiter.mNext = successor;
break;
}
//遍歷查找當前等待隊列第一個小于當前優(yōu)先級的位置
predecessor = successor;
successor = successor.mNext;
}
if (predecessor != null) {
//此時說明所有等待鎖的優(yōu)先級都不小于當前等待鎖的priority
//此時當前等待鎖為等待隊列的最后一個
predecessor.mNext = waiter;
} else {
//此時說明當前等待鎖為最高優(yōu)先級
//當前等待鎖為等待隊列的第一個
mConnectionWaiterQueue = waiter;
}
nonce = waiter.mNonce;
}
//鎖Lock結(jié)束
// 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 {
// Park the thread until a connection is assigned or the pool is closed.
// Rethrow an exception from the wait, if we got one.
// CONNECTION_POOL_BUSY_MILLIS默認32s
long busyTimeoutMillis = CONNECTION_POOL_BUSY_MILLIS;
long nextBusyTimeoutTime = waiter.mStartTime + busyTimeoutMillis;
for (; ; ) { //等待開始
// Detect and recover from connection leaks.
if (mConnectionLeaked.compareAndSet(true, false)) {
//處理已經(jīng)處于泄漏狀態(tài)的數(shù)據(jù)庫連接(沒有任何引用持有)
synchronized (mLock) {
wakeConnectionWaitersLocked();
}
}
// 使當前線程進入休眠,最長時間:busyTimeoutMillis * 1000000L,單位納秒
// 注意喚醒操作是在 releaseConnection 方法,此時有連接被釋放,會喚醒該休眠
// Wait to be unparked (may already have happened), a timeout, or interruption.
LockSupport.parkNanos(this, busyTimeoutMillis * 1000000L);
// Clear the interrupted flag, just in case.
Thread.interrupted();
// Check whether we are done waiting yet.
synchronized (mLock) {
throwIfClosedLocked();
// 在releaseConnection方法,如果有新的連接被釋放
// 此時按照等待隊列的優(yōu)先級獲取到需要的數(shù)據(jù)庫連接
final SQLiteConnection connection = waiter.mAssignedConnection;
final RuntimeException ex = waiter.mException;
if (connection != null || ex != null) {
//重新回收該等待鎖:ConnectionWaiter
recycleConnectionWaiterLocked(waiter);
if (connection != null) {
//當前等待鎖獲取到數(shù)據(jù)庫連接,直接返回
return connection;
}
throw ex; // rethrow!
}
//重新調(diào)整busyTimeoutMills
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);
}
}
}
雖然等待獲取連接的代碼有點多,但是并不難理解,先來看 obtainConnectionWaiterLocked 方法獲取一個數(shù)據(jù)庫連接等待鎖:
private ConnectionWaiter obtainConnectionWaiterLocked(Thread thread, long startTime,
int priority, boolean wantPrimaryConnection, String sql, int connectionFlags) {
ConnectionWaiter waiter = mConnectionWaiterPool;
if (waiter != null) {
//指向它的下一個,當前的被占用了
mConnectionWaiterPool = waiter.mNext;
waiter.mNext = null;
} else {
//否則創(chuàng)建
waiter = new ConnectionWaiter();
}
//當前線程
waiter.mThread = thread;
//等待開始時間
waiter.mStartTime = startTime;
//等待優(yōu)先級
waiter.mPriority = priority;
//是否需要主連接
waiter.mWantPrimaryConnection = wantPrimaryConnection;
//SQL語句
waiter.mSql = sql;
//需要的連接類型
waiter.mConnectionFlags = connectionFlags;
return waiter;
}
ConnectionWaiter 表示當前線程等待獲取數(shù)據(jù)庫連接的鎖對象,每一個等待獲取數(shù)據(jù)庫連接的線程都會關聯(lián)一個 ConnectionWatier,當有連接被釋放時,根據(jù)等待優(yōu)先級對應的 ConnectionWaiter 獲取到數(shù)據(jù)庫連接,此時即可返回完成數(shù)據(jù)庫訪問操作。
ConnectionWaiter 是個單向鏈表結(jié)構(gòu),而且 SQLiteConnectionPool 內(nèi)部還對其進行復用:
//回收ConnectionWaiter
private void recycleConnectionWaiterLocked(ConnectionWaiter waiter) {
waiter.mNext = mConnectionWaiterPool;
waiter.mThread = null;
waiter.mSql = null;
waiter.mAssignedConnection = null;
waiter.mException = null;
waiter.mNonce += 1;
mConnectionWaiterPool = waiter;
}
等待的優(yōu)先級 getPriority 方法與數(shù)據(jù)庫操作類型無關(讀/寫操作),與當前操作的線程有關,該值只有 0/1 兩種情況。
/**
* CONNECTION_FLAG_INTERACTIVE == 0100
* 如果查詢操作:0001 & 0100 或 0101(主線程) & 0100,如果是在主線程此時返回 1,否則 0
* 寫操作:0010 & 0100 或 0110(主線程) & 0100,如果是主線程此時返回 1,否則 0
* */
private static int getPriority(int connectionFlags) {
return (connectionFlags & CONNECTION_FLAG_INTERACTIVE) != 0 ? 1 : 0;
}
死循環(huán) for(; ; ) 一直等待有數(shù)據(jù)庫連接被釋放,LockSupport.parkNanos 會使當前等待線程進入 wait 狀態(tài),如果有新的連接被釋放,會根據(jù) ConnectionWaiter 喚醒對應的等待線程,該處理機制可以說是非常高效的。能夠有效解決等待獲取連接時間長短的問題。
連接的釋放
連接的釋放就是把執(zhí)行完任務的連接緩存到數(shù)據(jù)庫連接池(實際是 List 集合),并進行 unpark 通知正在等待獲取連接的線程。如何判斷當前連接任務已經(jīng)執(zhí)行完了呢?
//在SQLiteSession中執(zhí)行任務時acquireConnection獲取數(shù)據(jù)庫連接
acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
try {
return mConnection.executeForCursorWindow(sql, bindArgs,
window, startPos, requiredPos, countAllRows,
cancellationSignal); // might throw
} finally {
//任務執(zhí)行完成一定釋放該數(shù)據(jù)庫連接
releaseConnection(); // might throw
}
釋放數(shù)據(jù)庫連接操作最終調(diào)用到 SQLiteConnectionPool 中:
public void releaseConnection(SQLiteConnection connection) {
synchronized (mLock) {
if (mIdleConnectionHandler != null) {
//重新加入超時關閉機制
mIdleConnectionHandler.connectionReleased(connection);
}
//移除正在執(zhí)行任務的標志, 前面已經(jīng)分析過mAcquiredConnections是WeakHashMap
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) {
//如果數(shù)據(jù)庫已經(jīng)關閉,此時直接關閉該連接
closeConnectionAndLogExceptionsLocked(connection);
} else if (connection.isPrimaryConnection()) { //如果是主鏈接
//如果連接狀態(tài)不是要關閉
if (recycleConnectionLocked(connection, status)) {
assert mAvailablePrimaryConnection == null;
//mAvailablePrimaryConnection == null,賦值給其成員
mAvailablePrimaryConnection = connection;
}
//喚醒等待獲取連接的線程
wakeConnectionWaitersLocked();
} else if (mAvailableNonPrimaryConnections.size() >= mMaxConnectionPoolSize - 1) {
//如果是非主連接,此時緩存數(shù)量已經(jīng)大于允許的最大連接數(shù)量直接關閉,-1是考慮主連接
closeConnectionAndLogExceptionsLocked(connection);
} else {
if (recycleConnectionLocked(connection, status)) {
//加入到緩存池(List集合)
mAvailableNonPrimaryConnections.add(connection);
}
//喚醒等待獲取連接的線程
wakeConnectionWaitersLocked();
}
}
}
if(!mIsOpen) 表示當前連接池已經(jīng)被關閉了,此時要直接關閉釋放的數(shù)據(jù)庫連接。否則根據(jù)連接的類型(主連接 / 非主連接)將其加入對應的緩存。根據(jù)當前等待隊列的優(yōu)先級
并喚醒等待獲取連接的線程 wakeConnectionWaitersLocked 方法如下:
private void wakeConnectionWaitersLocked() {
// Unpark all waiters that have requests that we can fulfill.
// This method is designed to not throw runtime exceptions, although we might send
// a waiter an exception for it to rethrow.
ConnectionWaiter predecessor = null;
ConnectionWaiter waiter = mConnectionWaiterQueue;
boolean primaryConnectionNotAvailable = false;
boolean nonPrimaryConnectionNotAvailable = false;
while (waiter != null) {
boolean unpark = false;
if (!mIsOpen) {
//數(shù)據(jù)庫已經(jīng)關閉
unpark = true;
} else {
try {
SQLiteConnection connection = null;
if (!waiter.mWantPrimaryConnection && !nonPrimaryConnectionNotAvailable) {
//嘗試獲取非主連接
connection = tryAcquireNonPrimaryConnectionLocked(
waiter.mSql, waiter.mConnectionFlags); // might throw
if (connection == null) {
//表示當前獲取不到非主連接
nonPrimaryConnectionNotAvailable = true;
}
}
if (connection == null && !primaryConnectionNotAvailable) {
//嘗試獲取主連接
connection = tryAcquirePrimaryConnectionLocked(
waiter.mConnectionFlags); // might throw
if (connection == null) {
//此時表示主連接也獲取不到
primaryConnectionNotAvailable = true;
}
}
if (connection != null) {
//獲取到連接
waiter.mAssignedConnection = connection;
unpark = true;
} else if (nonPrimaryConnectionNotAvailable && primaryConnectionNotAvailable) {
//如果獲取不到連接就到此為止吧
//此時waitForConnection仍然繼續(xù)等待
break;
}
} catch (RuntimeException ex) {
// Let the waiter handle the exception from acquiring a connection.
waiter.mException = ex;
unpark = true;
}
}
final ConnectionWaiter successor = waiter.mNext;
if (unpark) {
//
if (predecessor != null) {
predecessor.mNext = successor;
} else {
mConnectionWaiterQueue = successor;
}
waiter.mNext = null;
//喚醒等待獲取連接線程
LockSupport.unpark(waiter.mThread);
} else {
predecessor = waiter;
}
waiter = successor;
}
}
mConnectionWaiterQueue 為當前等待隊列,根據(jù)當前需要的連接類型依次嘗試獲取非主連接或主連接。unpark 表示當前是否獲取到連接,如果仍然未獲取到,此時 waitForConnection 將繼續(xù)等待。否則方法的最后喚醒相應的等待線程,并重新調(diào)整當前等待隊列。
LockSupport.unpark(watier.mThread)
總結(jié)
為了進一步提高并發(fā)性能,我們還可以打開 WAL(Write-Ahead-Logging)模式。WAL 模式會將修改的數(shù)據(jù)單獨寫到一個 WAL 文件中,同時也會引入了 WAL 日志文件鎖。通過 WAL 模式讀和寫可以完全地并發(fā)執(zhí)行,不會互相阻塞。
總的來說通過連接池與 WAL 模式,我們可以很大程度上增加 SQLite 的讀寫并發(fā),大大減少由于并發(fā)導致的等待耗時,錦衣大家在應用中可以嘗試開啟。
以上便是個人在學習 SQLiteConnectionPool 時的心得和體會,文中如有不妥或更好的分析結(jié)果,還請大家指出!
文章如果對你有幫助,就請留個贊吧!