最近碰到一个跟SQLiteConnectionPool相关的问题,然后就把这一块逻辑捋了一遍,做了一点笔记记录如下。
跟SQLiteConnectionPool相关的几个大类有SQLiteDatabase、SQLiteConnection、SQLiteSession,搞清楚这几个类之间的关系,然后重点理解下SQLiteConnectionPool中获取/释放连接函数就基本搞懂了这一块逻辑,碰到问题基本能搞定。
一、SQLiteDatabase、SQLiteConnectionPool、SQLiteConnection、SQLiteSession四者之间的关系
01-07 00:39:38.310 911 911 W guoqifa : at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:720)
01-07 00:39:38.310 911 911 W guoqifa : at android.app.ContextImpl.openOrCreateDatabase(ContextImpl.java:1294)
01-07 00:39:38.310 911 911 W guoqifa : at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:223)
01-07 00:39:38.310 911 911 W guoqifa : at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:163)
public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,
DatabaseErrorHandler errorHandler) {
SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler);
db.open(); //open()函数中继续调用openInner(),openInner()函数中继续调用SQLiteConnectionPool.open()来创建一个SQLiteConnectionPool对象;
return db;
}
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(); //此处会紧接着创建一个主连接SQLiteConnection
return pool;
}
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");
}
01-07 00:39:38.370 911 911 W guoqifa : at android.database.sqlite.SQLiteDatabase.createSession(SQLiteDatabase.java:384)
01-07 00:39:38.370 911 911 W guoqifa : at android.database.sqlite.SQLiteDatabase$1.initialValue(SQLiteDatabase.java:92)
01-07 00:39:38.370 911 911 W guoqifa : at android.database.sqlite.SQLiteDatabase$1.initialValue(SQLiteDatabase.java:89)
01-07 00:39:38.370 911 911 W guoqifa : at java.lang.ThreadLocal$Values.getAfterMiss(ThreadLocal.java:430)
01-07 00:39:38.370 911 911 W guoqifa : at java.lang.ThreadLocal.get(ThreadLocal.java:65)
01-07 00:39:38.370 911 911 W guoqifa : at android.database.sqlite.SQLiteDatabase.getThreadSession(SQLiteDatabase.java:373)
01-07 00:39:38.370 911 911 W guoqifa : at android.database.sqlite.SQLiteProgram.(SQLiteProgram.java:58)
01-07 00:39:38.370 911 911 W guoqifa : at android.database.sqlite.SQLiteStatement.(SQLiteStatement.java:31)
01-07 00:39:38.370 911 911 W guoqifa : at android.database.sqlite.SQLiteDatabase.compileStatement(SQLiteDatabase.java:1030)
01-07 00:39:38.370 911 911 W guoqifa : at android.database.DatabaseUtils.longForQuery(DatabaseUtils.java:857)
01-07 00:39:38.370 911 911 W guoqifa : at android.database.sqlite.SQLiteDatabase.getVersion(SQLiteDatabase.java:900)
01-07 00:39:38.370 911 911 W guoqifa : at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:241)
01-07 00:39:38.370 911 911 W guoqifa : at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:163)
public final class SQLiteConnection implements CancellationSignal.OnCancelListener {
......
// The native SQLiteConnection pointer. (FOR INTERNAL USE ONLY)
private long mConnectionPtr;
......
}
public SQLiteConnection acquireConnection(String sql, int connectionFlags,
CancellationSignal cancellationSignal) {
return waitForConnection(sql, connectionFlags, cancellationSignal);
}
private SQLiteConnection waitForConnection(String sql, int connectionFlags,
CancellationSignal cancellationSignal) {
final boolean wantPrimaryConnection = //如果cancellationSignal带了CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY,则是请求主连接;
(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) { //请求非主连接,即调用tryAcquireNonPrimaryConnectionLocked()来尝试获取一个非主连接;
connection = tryAcquireNonPrimaryConnectionLocked(
sql, connectionFlags); // might throw
}
if (connection == null) { //如果是请求主连接、或请求非主连接时tryAcquireNonPrimaryConnectionLocked无法返回一个非主连接,便尝试获取主连接;
connection = tryAcquirePrimaryConnectionLocked(connectionFlags); // might throw
}
if (connection != null) { //获取到可用SQLiteConnection便立即返回,否则继续走下面逻辑进行等待可用连接;
return connection;
}
//如果无法获取到空闲可用连接(原因:连接被占用,也无法创建新连接),那么就会加入等待队列,等待连接被释放,然后重新获取;
// No connections available. Enqueue a waiter in priority order.
final int priority = getPriority(connectionFlags);
final long startTime = SystemClock.uptimeMillis();
waiter = obtainConnectionWaiterLocked(Thread.currentThread(), startTime,
priority, wantPrimaryConnection, sql, connectionFlags); //获取连接等待对象ConnectionWaiter;
ConnectionWaiter predecessor = null;
ConnectionWaiter successor = mConnectionWaiterQueue; //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 {
// Park the thread until a connection is assigned or the pool is closed.
// Rethrow an exception from the wait, if we got one.
long busyTimeoutMillis = CONNECTION_POOL_BUSY_MILLIS;
long nextBusyTimeoutTime = waiter.mStartTime + busyTimeoutMillis; //mStartTime保存着该“连接等待”对象的创建时间点;
for (;;) {
// Detect and recover from connection leaks.
if (mConnectionLeaked.compareAndSet(true, false)) { //mConnectionLeaked在SQLiteConnection对象被finalize()回收时设置true;
synchronized (mLock) {
//mConnectionLeaked为true,代表可能某个SQLiteConnection被收回了。那么就调用wakeConnectionWaitersLocked()来获取空闲可用的连接。如果可获取到空闲可用连接,那么就将mConnectionWaiterQueue链表第一个ConnectionWaiter对象remove,然后将ConnectionWaiter对象的mAssignedConnection设置为获取到的空闲可用SQLiteConnection,如果是发生异常则将ConnectionWaiter对象的mException置为捕获到的异常,最后调用LockSupport.unpark(waiter.mThread)来唤醒waiter对象所在的线程;
wakeConnectionWaitersLocked();
}
}
// Wait to be unparked (may already have happened), a timeout, or interruption.
LockSupport.parkNanos(this, busyTimeoutMillis * 1000000L); //许可默认是被占用的 ,调用park()时获取不到许可,所以进入阻塞状态。可被LockSupport.unpark()唤醒,或超时30s唤醒;
// Clear the interrupted flag, just in case.
Thread.interrupted();
// Check whether we are done waiting yet.
synchronized (mLock) {
throwIfClosedLocked();
//执行到这里代表当前线程被LockSupport.unpark(),或超时30s唤醒,这时检查下当前waiter对象的mAssignedConnection、mException成员,如果不为null,那么就回收这个ConnectionWaiter对象(清理状态,并保存到mConnectionWaiterPool链表中)。如果是mAssignedConnection不为null,那么返回这个新获取的连接;如果是mException不为null,那么把这个异常重新抛出来;
final SQLiteConnection connection = waiter.mAssignedConnection;
final RuntimeException ex = waiter.mException;
if (connection != null || ex != null) {
recycleConnectionWaiterLocked(waiter);
if (connection != null) {
return connection;
}
throw ex; // rethrow!
}
final long now = SystemClock.uptimeMillis();
if (now < nextBusyTimeoutTime) { //当前线程被唤醒,没有获取到SQLiteConnection,并且当前时间点未到timeout时间点。那么计算一个新的timeout,重新block;
busyTimeoutMillis = now - nextBusyTimeoutTime;
} else { //当前线程被唤醒,没有获取到SQLiteConnection,并且到了timeout时间点。那么继续给一个30s的timeout,重新block;
logConnectionPoolBusyLocked(now - waiter.mStartTime, connectionFlags); //打印日志;
busyTimeoutMillis = CONNECTION_POOL_BUSY_MILLIS;
nextBusyTimeoutTime = now + busyTimeoutMillis;
}
}
}
} finally {
// Remove the cancellation listener.
if (cancellationSignal != null) {
cancellationSignal.setOnCancelListener(null);
}
}
}
尝试获取非主连接。
private SQLiteConnection tryAcquireNonPrimaryConnectionLocked(
String sql, int connectionFlags) {
// Try to acquire the next connection in the queue.
SQLiteConnection connection;
final int availableCount = mAvailableNonPrimaryConnections.size(); //mAvailableNonPrimaryConnections保存着空闲可用的非主连接;
if (availableCount > 1 && sql != null) { //空闲可用非主连接大于1时,试图找一个sql相同的连接;
// 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);
if (connection.isPreparedStatementInCache(sql)) {
mAvailableNonPrimaryConnections.remove(i);
finishAcquireConnectionLocked(connection, connectionFlags); // might throw
return connection;
}
}
}
if (availableCount > 0) { //空闲非主连接数量为1,或数量大于1时但没有找到相同sql的连接时,直接返回数组最后一个空闲非主连接;
// Otherwise, just grab the next one.
connection = mAvailableNonPrimaryConnections.remove(availableCount - 1);
finishAcquireConnectionLocked(connection, connectionFlags); // might throw
return connection;
}
//没有空闲可用非主连接,进行扩展;
// Expand the pool if needed.
int openConnections = mAcquiredConnections.size(); //mAcquiredConnections中保存着被正在别的线程正在使用的连接;
if (mAvailablePrimaryConnection != null) { //如果主连接空闲可用,openConnections加1,这是为什么呢?因为代码执行到这里,总的打开连接数=被占用的连接数+空闲可用的非主连接数+空闲可用主连接,此刻被占用的连接数=mAcquiredConnections.size(),空闲可用的非主连接数=0
openConnections += 1;
}
if (openConnections >= mMaxConnectionPoolSize) { //总打开连接数已达到最大值,便无法扩展非主连接;
return null;
}
connection = openConnectionLocked(mConfiguration, //创建一个非主连接,并调用finishAcquireConnectionLocked()标记该连接被占用;
false /*primaryConnection*/); // might throw
finishAcquireConnectionLocked(connection, connectionFlags); // might throw
return connection;
}
3、SQLiteConnectionPool.tryAcquirePrimaryConnectionLocked()
private SQLiteConnection tryAcquirePrimaryConnectionLocked(int connectionFlags) {
// If the primary connection is available, acquire it now.
SQLiteConnection connection = mAvailablePrimaryConnection; //mAvailablePrimaryConnection保存着空闲可用的主连接;
if (connection != null) { //如果主连接空闲可用,标记为已被占用,然后返回主连接;
mAvailablePrimaryConnection = null;
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()) {
if (acquiredConnection.isPrimaryConnection()) { //从被占用连接数组中检索主连接,如果存在就return null;
return null;
}
}
//不存在主连接,那么创建一个主连接,并标记为被占用;
// Uhoh. No primary connection! Either this is the first time we asked
// for it, or maybe it leaked?
connection = openConnectionLocked(mConfiguration,
true /*primaryConnection*/); // might throw
finishAcquireConnectionLocked(connection, connectionFlags); // might throw
return connection;
}
public void releaseConnection(SQLiteConnection connection) {
synchronized (mLock) {
AcquiredConnectionStatus status = mAcquiredConnections.remove(connection); //从被占用连接列表mAcquiredConnections中remove;
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) { //mIsOpen表示数据库打开状态,为false代表已经被关闭了;
closeConnectionAndLogExceptionsLocked(connection);
} else if (connection.isPrimaryConnection()) { //如果释放的是主连接,那么将该连接进行回收,回收成功便将mAvailablePrimaryConnection重新指向主连接,同时调用wakeConnectionWaitersLocked()唤醒等待线程;
if (recycleConnectionLocked(connection, status)) {
assert mAvailablePrimaryConnection == null;
mAvailablePrimaryConnection = connection;
}
wakeConnectionWaitersLocked();
} else if (mAvailableNonPrimaryConnections.size() >= mMaxConnectionPoolSize - 1) { //如果释放的是非主连接,且空闲可用非主连接数大于max-1,那么关闭这个连接;
closeConnectionAndLogExceptionsLocked(connection);
} else { //如果释放的是非主连接,且数量没超,那么回收这个非主连接,并加到mAvailableNonPrimaryConnections这个空闲可用非主连接列表中,并唤醒等待线程;
if (recycleConnectionLocked(connection, status)) {
mAvailableNonPrimaryConnections.add(connection);
}
wakeConnectionWaitersLocked();
}
}
}
1、一个SQLiteOpenHelper打开的数据库对应有一个SQLiteDatabase对象;
2、一个SQLiteDatabase对应有一个连接池SQLiteConnectionPool;
3、一个连接池SQLiteConnectionPool对应有一个主连接SQLiteConnection;
4、每个调用过SQLiteDatabase.getThreadSession()的线程均有自己的SQLiteSession对象,保存在线程私有数据中;每个SQLiteSession某一时刻最多只有一个SQLiteConnection,需要时从连接池中获取,用完返还;
5、每个Java层SQLiteConnection对象均对应有一个native层SQLiteConnection对象,对数据库的操作就是通过这个native层SQLiteConnection对象调用sqlite库来完成的;
6、SQLiteSession从连接池SQLiteConnectionPool中获取的空闲可用的连接保存在SQLiteSession.mConnection中;
7、SQLiteConnectionPool.acquireConnection()获取空闲可用的连接,SQLiteConnectionPool.releaseConnection()释放被占用的连接;
8、Android SQLite中,数据库操作会经过SQLiteDatabase-->SQLiteSession-->SQLiteConnection(Java)-->SQLiteConnection(Native);
9、连接池SQLiteConnectionPool中,确保只有一个PrimaryConnection。
10、申请连接和释放连接一定要是一一对应的。申请了,一定要释放。当然,对于应用程序来说,只有beginTranscation需要手动去释放,也就是调用endTranscation,而且必须要调用(一般写在finally里面)。