Android中使用sqlite,使用最多的類莫過於SQLiteOpenHelper及SQLiteDatabased兩個類。使用最多的操作莫過於建立開啟資料庫、操作資料兩種操作,後者最長用的是insert delete update、query兩種操作。其中,query即select操作又牽扯到cursor等。
上述操作主要涉及SQLiteDatabase SQLiteSession SQLiteConnectionPool SQLiteConnection四個大類。本文將對Android操作sqlite的內部流程做簡要分析。
1、主要類成員變數
public final class SQLiteDatabase extends SQLiteClosable { private static WeakHashMap sActiveDatabases = new WeakHashMap(); // 儲存所有開啟的資料庫的引用 private final ThreadLocal mThreadSession = new ThreadLocal() { @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 mAvailableNonPrimaryConnections = new ArrayList(); //強引用,非主串連 private SQLiteConnection mAvailablePrimaryConnection; // 主串連 只有一個 private final WeakHashMap mAcquiredConnections = new WeakHashMap(); //弱引用,已取得的串連 ……}
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 等,具體如下。
<喎?http://www.bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4KPGgyPjOhomV4ZWNTUUy1xLX308PB97PMPC9oMj4K0tRTUUxpdGVEYXRhYmFzZS5leGVjU1FMzqrA/Txicj4KPHA+PC9wPgo8cD48L3A+CjxwcmUgY2xhc3M9"brush:java;"> 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相同的,如果沒有相同的擷取池中最後一個,如果池子已經空了(此時多個線程同時用著多個串連),建立一個非主串連。
⑥ 具體關係如下