上述操作主要涉及SQLiteDatabase SQLiteSession SQLiteConnectionPool SQLiteConnection四个大类。本文将对Android操作sqlite的内部流程做简要分析。
1、主要类成员变量
public final class SQLiteDatabase extends SQLiteClosable {
private static WeakHashMap<SQLiteDatabase, Object> sActiveDatabases =
new WeakHashMap<SQLiteDatabase, Object>(); // 存储所有打开的数据库的引用
private final ThreadLocal<SQLiteSession> mThreadSession = new ThreadLocal<SQLiteSession>() {
@Override
protected SQLiteSession initialValue() { // 每个线程有自己的一份mThreadSeesion
return createSession();
}
};
private final CursorFactory mCursorFactory; // Cursor工厂类,为了自定义Cursor
private final SQLiteDatabaseConfiguration mConfigurationLocked; // 数据库的配置
private SQLiteConnectionPool mConnectionPoolLocked; // 数据库连接池
……
}
public final class SQLiteSession {
private final SQLiteConnectionPool mConnectionPool; // 连接池
private SQLiteConnection mConnection; // 连接
private int mConnectionFlags;
private int mConnectionUseCount;
private Transaction mTransactionPool; // 事务池
private Transaction mTransactionStack; // 事务栈
……
}
public final class SQLiteConnectionPool implements Closeable {
private final SQLiteDatabaseConfiguration mConfiguration;
private int mMaxConnectionPoolSize;
private boolean mIsOpen;
private int mNextConnectionId;
private ConnectionWaiter mConnectionWaiterPool; // 连接等待池 其实是由 等待的连接 组成的链
private ConnectionWaiter mConnectionWaiterQueue; // 连接等待队列
private final ArrayList<SQLiteConnection> mAvailableNonPrimaryConnections =
new ArrayList<SQLiteConnection>(); //强引用,非主连接
private SQLiteConnection mAvailablePrimaryConnection; // 主连接 只有一个
private final WeakHashMap<SQLiteConnection, AcquiredConnectionStatus> mAcquiredConnections =
new WeakHashMap<SQLiteConnection, AcquiredConnectionStatus>(); //弱引用,已取得的连接
……
}
public final class SQLiteConnection implements CancellationSignal.OnCancelListener {
private final SQLiteConnectionPool mPool;
private final SQLiteDatabaseConfiguration mConfiguration;
private final int mConnectionId;
private final boolean mIsPrimaryConnection;
private final boolean mIsReadOnlyConnection;
private final PreparedStatementCache mPreparedStatementCache; //stmt的缓存 强引用
private PreparedStatement mPreparedStatementPool;
private int mConnectionPtr; // native层SQLiteConnection的指针
……
}
2、打开数据库时的调用情况
我们使用SQLiteOpenHelper时:
①
新建一个帮助类,getReadableDatabase或getWritableDatabase时,到②的第二条新打开
如果已经有了帮助类并且使用过,如果已经手动mDatabase.close过,到②的第二条新打开
getReadableDatabase时,无论上次使用是getReadableDatabase还是getWritableDatabase,会直接返回mDatabase,
getWritableDatabase时,如果上次是getWritableDatabase依然直接返回mDataBase,如果上次是getReadableDatabase,到②的第一条以读写模式打开。
②
如果已经有了帮助类,如果需要写入但现在是只读,即上次是getReadableDatabas这次是getWritableDatabase,则以读写模式重新打开db.reopenreadwrite
否则,如果getReadableDatabase,通过SQLiteDatabase.openDatabase打开只读数据库;如果getWritableDatabase,通过mContext.openOrCreateDatabase,最终仍通过SQLiteDatabase.openDatabase打开,此时flag已经变作CREATE_IF_NECESSARY。
到SQLiteDatabase中看下
public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,
DatabaseErrorHandler errorHandler) {
SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler);
db.open(); // open会调用openInner 省略
return db;
}
private void openInner() {
synchronized (mLock) {
assert mConnectionPoolLocked == null;
mConnectionPoolLocked = SQLiteConnectionPool.open(mConfigurationLocked);
mCloseGuardLocked.open("close");
}
synchronized (sActiveDatabases) {
sActiveDatabases.put(this, null); // 放入sActiveDatabases
}
}
SQLiteDatabase 持有自己的连接池,在open时获取到,在SQLiteConnectionPool中
public static SQLiteConnectionPool open(SQLiteDatabaseConfiguration configuration) {
SQLiteConnectionPool pool = new SQLiteConnectionPool(configuration);
pool.open();
return pool;
}
private void open() {
mAvailablePrimaryConnection = openConnectionLocked(mConfiguration,
true /*primaryConnection*/); // 打开连接池 其实是打开一个主连接
mIsOpen = true;
mCloseGuard.open("close");
}
private SQLiteConnection openConnectionLocked(SQLiteDatabaseConfiguration configuration,
boolean primaryConnection) {
final int connectionId = mNextConnectionId++;
return SQLiteConnection.open(this, configuration, // 通过调用connection.open()
connectionId, primaryConnection);
}
SQLiteConnection调用的方法就是native层面了,open方法也比较简单。
private void open() {
//--- !!! nativeOpen 并设置相应参数
mConnectionPtr = nativeOpen(mConfiguration.path, mConfiguration.openFlags,
mConfiguration.label,
SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME);
setPageSize();
setForeignKeyModeFromConfiguration();
setWalModeFromConfiguration();
setJournalSizeLimit();
setAutoCheckpointInterval();
setLocaleFromConfiguration();
}
流程很简单,令人疑惑的是SQLiteConnectionPool在这里的作用。更令人疑惑的是连接池此时呈现出来的仅仅是一个主连接。
SQLiteConnectionPool中最为重要的成员是acquireConnection,下图表示了调用该成员的类及方法
可以看到,部分是开始事务时相关的方法、部分是准备statement时用到、部分是query时cursor用到。还有部分是与带返回结果的及不带返回结果的sql相关的操作,但这部分没有外部调用,也没有内部调用。其实不是的,最后一部分是为常用的insert delete update 等,具体如下。
3、execSQL的调用流程
以SQLiteDatabase.execSQL为例 public void execSQL(String sql) throws SQLException { //执行单条 无返回值 非select的sql
executeSql(sql, null);
}
private int executeSql(String sql, Object[] bindArgs) throws SQLException {
……
SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs); // 获取statement
try {
return statement.executeUpdateDelete();
} finally {
statement.close();
}
}
public int executeUpdateDelete() {
acquireReference();
try {
return getSession().executeForChangedRowCount( // getSession在此出现
getSql(), getBindArgs(), getConnectionFlags(), null);
} catch (SQLiteDatabaseCorruptException ex) {
onCorruption();
throw ex;
} finally {
releaseReference();
}
}
protected final SQLiteSession getSession() { // SQLiteProgram中
return mDatabase.getThreadSession();
}
SQLiteSession getThreadSession() { // SQLiteDatabase中
return mThreadSession.get(); // 和第1部分对应起来了 每个线程有自己的Session
}
继续查看 getSession().executeForChangedRowCount
// SQLiteSession中
public int executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags,
CancellationSignal cancellationSignal) {
……
acquireConnection(sql, connectionFlags, cancellationSignal); // 获取连接
try {
return mConnection.executeForChangedRowCount(sql, bindArgs, // 通过connection执行
cancellationSignal);
} finally {
releaseConnection();
}
}
// SQLiteSession中
private void acquireConnection(String sql, int connectionFlags,
CancellationSignal cancellationSignal) {
if (mConnection == null) {
assert mConnectionUseCount == 0;
mConnection = mConnectionPool.acquireConnection(sql, connectionFlags, // 连接池中获取连接
cancellationSignal); // might throw
mConnectionFlags = connectionFlags;
}
mConnectionUseCount += 1;
}
总算找到SQLiteConnectionPool.acquireConnection了
// SQLiteConnectionPool中
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 = // 是否需要主连接,通过Flag得到
(connectionFlags & CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY) != 0;
final ConnectionWaiter waiter;
synchronized (mLock) {
SQLiteConnection connection = null;
if (!wantPrimaryConnection) { // 尝试获取非主连接
connection = tryAcquireNonPrimaryConnectionLocked(
sql, connectionFlags);
}
if (connection == null) { //--- 尝试获取主连接
connection = tryAcquirePrimaryConnectionLocked(connectionFlags);
}
if (connection != null) {
return connection;
}
// 若得不到连接,生成一个waiter
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;
}
……
}
这里根据connectionFlags判定是否要获得主连接,如第2步分析数据库open时,就是主连接
private SQLiteConnection tryAcquirePrimaryConnectionLocked(int connectionFlags) {
// 主连接可获取 直接返回
SQLiteConnection connection = mAvailablePrimaryConnection;
if (connection != null) {
mAvailablePrimaryConnection = null;
finishAcquireConnectionLocked(connection, connectionFlags);
return connection;
}
// 主连接存在并且刚刚获取过,则返回空
for (SQLiteConnection acquiredConnection : mAcquiredConnections.keySet()) {
if (acquiredConnection.isPrimaryConnection()) {
return null;
}
}
// 主连接不存在 新建 只可能在第一次访问时发生
connection = openConnectionLocked(mConfiguration,
true /*primaryConnection*/);
finishAcquireConnectionLocked(connection, connectionFlags);
return connection;
}
如果获取主连接时返回空,会加入等待队列,在一定时间后重试获取,最后仍不能得到会抛出异常。 其他时候,例如本节的update,将依靠statement的属性,由其一个成员变量mReadOnly来表示,实际由sql转换为stmt即prepare时确定。例如begin commit 命令将是false。
private SQLiteConnection tryAcquireNonPrimaryConnectionLocked(
String sql, int connectionFlags) {
// 尝试获取非主连接队列中的下一个连接
SQLiteConnection connection;
final int availableCount = mAvailableNonPrimaryConnections.size();
if (availableCount > 1 && sql != null) {
// 如果sql!=null 优先使用缓存中含有相同sql语句的connection
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) {
// 否则获取下一个连接,其实是pool最后一个
connection = mAvailableNonPrimaryConnections.remove(availableCount - 1);
finishAcquireConnectionLocked(connection, connectionFlags); // might throw
return connection;
}
//--- 若有需要即池中无连接时,扩展连接池,
int openConnections = mAcquiredConnections.size();
if (mAvailablePrimaryConnection != null) {
openConnections += 1;
}
if (openConnections >= mMaxConnectionPoolSize) {
return null;
}
connection = openConnectionLocked(mConfiguration, // 新打开一个非主连接,真正连接到nativeOpen
false /*primaryConnection*/);
finishAcquireConnectionLocked(connection, connectionFlags); // 会将新建立的connection放入mAcquiredConnections
return connection;
}
那么mAvailableNonPrimaryConnections中的连接是怎么来的呢?
但凡使用acquireConnection后,必须使用releaseConnection
// SQLiteSession中
private void releaseConnection() {
assert mConnection != null;
assert mConnectionUseCount > 0;
if (--mConnectionUseCount == 0) {
try {
mConnectionPool.releaseConnection(mConnection); // might throw
} finally {
mConnection = null;
}
}
}
// SQLiteConnectionPool中
public void releaseConnection(SQLiteConnection connection) {
synchronized (mLock) {
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();
}
}
}
一个connection或者属于SQLiteConnectionPool或者属于SQLiteSession
SQLiteSession通过acquire从SQLiteConnectionPool获取connection,通过release将其返还。
4、总结
① Android SQLite中,多数操作需经过 SQLiteDatabase -> SQLiteSession -> SQLiteConnectionPool -> SQLiteConnection
② SQLiteOpenHelper类能够帮助实现一个实例里最多只有一个SQLiteDatabase对象,无论经过几次getReadableDatabase getWritableDatabase,是否经过了db.close()。
③ SQLiteDatabase.openDatabase的过程是构建SQLiteDatabase对象的过程,实质是构建SQLiteDatabase的成员变量SQLiteConnectionPool的过程,该过程是一个获取primaryConnection的过程。
④ 每个线程有自己的SQLiteSession且只有一个,每个SQLiteSession在某一时刻最多只有一个SQLiteConnection(需要时从连接池获取,用完返还),保证了一个线程在某一时刻只有一个SQLiteConnection连接到某一SQLiteDatabase。事务同样通过Session来实现,故线程之间的事务是独立的。
⑤ SQLiteConnectionPool掌管某个SQLiteDatabase的连接池。确保PrimaryConnection只有一个,如果空闲则将其返回,如果正被其他session使用则返回空,如果没有则新建。对于非PrimaryConnection,将会在连接池中优先选取stmt相同的,如果没有相同的获取池中最后一个,如果池子已经空了(此时多个线程同时用着多个连接),新建一个非主连接。
⑥ 具体关系如下