Android SQLiteDatabase SQLiteSession SQLiteConnectionPool SQLiteConnection关系

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<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相同的,如果没有相同的获取池中最后一个,如果池子已经空了(此时多个线程同时用着多个连接),新建一个非主连接。

⑥ 具体关系如下




郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。