SQLiteConnectionPool学习笔记

    最近碰到一个跟SQLiteConnectionPool相关的问题,然后就把这一块逻辑捋了一遍,做了一点笔记记录如下。

    跟SQLiteConnectionPool相关的几个大类有SQLiteDatabase、SQLiteConnection、SQLiteSession,搞清楚这几个类之间的关系,然后重点理解下SQLiteConnectionPool中获取/释放连接函数就基本搞懂了这一块逻辑,碰到问题基本能搞定。

一、SQLiteDatabase、SQLiteConnectionPool、SQLiteConnection、SQLiteSession四者之间的关系

1、 SQLiteDatabase
    应用使用SQLiteDatabase来操作数据库。调用SQLiteOpenHelper. getWritableDatabase()/ getReadableDatabase()打开数据库时,便会调用到 SQLiteDatabase.openDatabase()来创建一个 SQLiteDatabase对象。
 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)

2、 SQLiteConnectionPool
    数据库连接池,管理SQLiteConnection。应用对数据库的操作被执行之前需获取一个SQLiteConnection连接,最终会通过SQLiteConnection所对应的native层SQLiteConnection对象来完成数据库的最终操作。 SQLiteDatabase对象创建完成后,立即调用SQLiteDatabase.open()函数来创建一个SQLiteConnectionPool对象。
    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;
    }

3、 SQLiteConnection(Java)
    数据库连接。线程操作数据库必须通过 SQLiteSession来获取一个 SQLiteConnection连接,通过 连接来执行数据库操作。创建连接池 SQLiteConnectionPool后,会立即调用其open()函数创建一个主连接SQLiteConnection。
    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");
    }

4、SQLiteSession
    线程数据库会话。线程操作数据必须通过SQLiteSession来获取一个SQLiteConnection连接,每个线程都有自己的SQLiteSession对象,属于线程私有数据,需要通过 SQLiteDatabase.getThreadSession()静态函数来获取。线程第一次调用getThreadSession()便会创建一个SQLiteSession对象。例如:进程PID= 911中tid= 911的线程第一次调用SQLiteDatabase.getThreadSession()时,便会创建一个SQLiteSession对象。tid=912的线程第一次调用SQLiteDatabase.getThreadSession()时,同样也会创建一个SQLiteSession对象。
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)

5、 SQLiteConnection(native
     数据库操作最终通过native SQLiteConnection对象调用sqlite库接口来完成。每个Java层 SQLiteConnection对象,都对应有一个native层SQLiteConnection对象,native层SQLiteConnection对象的地址保存在java SQLiteConnection. mConnectionPtr中。
public final class SQLiteConnection implements CancellationSignal.OnCancelListener {
    ......
    // The native SQLiteConnection pointer.  (FOR INTERNAL USE ONLY)
    private long mConnectionPtr;
    ......
}

二、重点函数分析
1、 SQLiteConnectionPool. acquireConnection()
    数据库连接分为主连接/非主连接。线程会话 SQLiteSession获取连接时,最终通过SQLiteConnectionPool.acquireConnection()来获取一个空闲可用的SQLiteConnection。
    public SQLiteConnection acquireConnection(String sql, int connectionFlags,
            CancellationSignal cancellationSignal) {
        return waitForConnection(sql, connectionFlags, cancellationSignal);
    }
    acquireConnection()直接调用 waitForConnection(),接下来重点分析这个函数,见备注。
    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);
            }
        }
    }

2、 SQLiteConnectionPool. tryAcquireNonPrimaryConnectionLocked()

    尝试获取非主连接。

    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;
    }

4、 SQLiteConnectionPool. releaseConnection()
    释放连接。应用获取连接操作完数据库后是需要释放连接的,因为连接是有最大数量限制的。
    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里面)


四、参考文章
1、Android SQLiteDatabase SQLiteSession SQLiteConnectionPool SQLiteConnection关系 http://2goo.info/weblog/detail/238107
2、Android sqlite数据库连接池连接异常分析: http://bbs.51cto.com/thread-1113117-1.html


你可能感兴趣的:(android系统层)