Android SQLiteDatabase SQLiteSession SQLiteConnectionPool SQ

來源:互聯網
上載者:User

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相同的,如果沒有相同的擷取池中最後一個,如果池子已經空了(此時多個線程同時用著多個串連),建立一個非主串連。

⑥ 具體關係如下




聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.