热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android应用框架层SQLite源码分析

Android应用框架层SQLite源码分析-概述  Android在应用框架层为开发者提供了SQLite相关操作接口,其归属于android.database.sqlite包底

概述

  Android 在应用框架层为开发者提供了 SQLite 相关操作接口,其归属于android.database.sqlite包底下,主要包含SQLiteProgram, SQLiteDatabase, SQLiteSession, SQLiteConnectionPoolSQLiteConnection等一些类。相比较 SQLite 提供的轻量级接口,应用框架层为开发者封装的 SQLite 抽象层显得更为复杂,但也为开发者屏蔽了更多细节,减小 SQLite 使用难度。

  在设计上,一个 SQLiteDatabase 持有一个 SQLiteConnectionPool, SQLiteConnectionPool 包含 n 个 SQLiteConnection,其根据日志模式的不同,连接池容量也不同。每个线程对于 SQLiteDatabase 的操作通过ThreadLocal创建的 SQLiteSession 来进行管理,而 SQLiteSession 进行操作时需要预先获得一个 SQLiteConnection。如果此时数据库连接池中的连接都被使用,那么会阻塞直到获得可用连接。

SQLiteDatabase

  SQLiteDatabase 提供了一系列管理数据库的方法,通过它我们可以进行增删改查和执行 SQLite 命令语句等操作。其高度封装了SQLiteSession, SQLiteConnectionPoolSQLiteConnection的执行细节,开发者仅需关心上层 API 的使用。

SQLiteDatabase 的打开

  SQLiteDatabase 提供了一系列open*函数,而这些函数的功能是打开数据库连接,区别在于传入的参数不同。

public static SQLiteDatabase openDatabase(@NonNull String path, @NonNull OpenParams openParams);

public static SQLiteDatabase openDatabase(@NonNull String path, @Nullable CursorFactory factory, @DatabaseOpenFlags int flags);

public static SQLiteDatabase openDatabase(@NonNull String path, @Nullable CursorFactory factory, @DatabaseOpenFlags int flags, @Nullable DatabaseErrorHandler errorHandler);

public static SQLiteDatabase openOrCreateDatabase(@NonNull String path, @Nullable CursorFactory factory);

public static SQLiteDatabase openOrCreateDatabase(@NonNull String path, @Nullable CursorFactory factory, @Nullable DatabaseErrorHandler errorHandler);

  对于数据库创建、版本升级等管理行为则被封装在SQLiteOpenHelper类中。SQLiteOpenHelper采用延迟初始化的方式来创建或打开数据库,开发者调用getReadableDatabasegetWritableDatabase取得一个SQLiteDatabase实例,而最终都会走到getDatabaseLocked方法。

private SQLiteDatabase getDatabaseLocked(boolean writable) {
    if (mDatabase != null) {
        // 数据库被关闭,此时将 SQLiteDatabase 对象置空,重新打开数据库
        if (!mDatabase.isOpen()) {
            // Darn!  The user closed the database by calling mDatabase.close().
            mDatabase = null;    
        } else if (!writable || !mDatabase.isReadOnly()) {
            // writable 为 false 说明请求只读数据库,满足要求
            // 数据库非只读,支持可读可写,满足要求
            return mDatabase;
        }
    }

    // 防止重复初始化
    if (mIsInitializing) {
        throw new IllegalStateException("getDatabase called recursively");
    }

    SQLiteDatabase db = mDatabase;
    try {
        mIsInitializing = true;

        if (db != null) {
            // 要求可写,但此时数据库以只读方式打开,需要重新以读写方式打开
            if (writable && db.isReadOnly()) {
                db.reopenReadWrite();
            }
        } else if (mName == null) {
            // 数据库名为空,说明要创建内存数据库
            db = SQLiteDatabase.createInMemory(mOpenParamsBuilder.build());
        } else {
            final File filePath = mContext.getDatabasePath(mName);
            // 创建者模式配置打开参数
            SQLiteDatabase.OpenParams params = mOpenParamsBuilder.build();
            try {
                db = SQLiteDatabase.openDatabase(filePath, params);
                // Keep pre-O-MR1 behavior by resetting file permissions to 660
                setFilePermissionsForDb(filePath.getPath());
            // SQLiteDatabase.OpenParams 没有配置 openFlags, 默认以读写方式打开数据库    
            } catch (SQLException ex) {
                // 打开数据库抛出异常,如果要求以可写方式打开,则抛出异常,否则尝试用只读方式打开数据库
                if (writable) {
                    throw ex;
                }
                Log.e(TAG, "Couldn't open " + mName
                        + " for writing (will try read-only):", ex);
                params = params.toBuilder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
                db = SQLiteDatabase.openDatabase(filePath, params);
            }
        }

        // onConfigure 回调,配置数据库相关设置
        onConfigure(db);

        // 获取当前数据库版本
        final int version = db.getVersion();
        // mNewVersion 为自行设置版本,两者不相等时,说明首次创建数据库或者升级或者降级数据库(一般是更改表结构,升级数据库)
        if (version != mNewVersion) {
            if (db.isReadOnly()) {
                throw new SQLiteException("Can't upgrade read-only database from version " +
                        db.getVersion() + " to " + mNewVersion + ": " + mName);
            }

            // 数据库版本小于最小支持版本,删库重建
            if (version > 0 && version  mNewVersion) {
                            onDowngrade(db, version, mNewVersion);
                        } else {
                            onUpgrade(db, version, mNewVersion);
                        }
                    }
                    db.setVersion(mNewVersion);
                    db.setTransactionSuccessful();
                } finally {
                    db.endTransaction();
                }
            }
        }

        // onOpen函数被回调
        onOpen(db);

        if (db.isReadOnly()) {
            Log.w(TAG, "Opened " + mName + " in read-only mode");
        }

        mDatabase = db;
        return db;
    } finally {
        mIsInitializing = false;
        if (db != null && db != mDatabase) {
            db.close();
        }
    }
}

  数据库打开相关配置参数设置:

private OpenParams(int openFlags, CursorFactory cursorFactory,
        DatabaseErrorHandler errorHandler, int lookasideSlotSize, int lookasideSlotCount,
        long idleConnectionTimeout, String journalMode, String syncMode) {
    // 打开模式
    mOpenFlags = openFlags;
    mCursorFactory = cursorFactory;
    // 错误处理器,在抛出 SQLiteDatabaseCorruptionException 时被使用
    mErrorHandler = errorHandler;
    // 默认120kb
    mLookasideSlotSize = lookasideSlotSize;
    // 默认100
    mLookasideSlotCount = lookasideSlotCount;
    // 数据库空闲连接超时时间
    mIdleCOnnectionTimeout= idleConnectionTimeout;
    // 日志模式
    mJournalMode = journalMode;
    // 同步模式
    mSyncMode = syncMode;
}

  SQLiteOpenHelper 调用的SQLiteDatabase#Open*函数最终会打开一个数据库主连接,这个主连接可用来读写,在 SQLiteConnectionPool 部分会进行详细的介绍。

private void open() {
    try {
        try {
            openInner();
        } catch (RuntimeException ex) {
            if (SQLiteDatabaseCorruptException.isCorruptException(ex)) {
                Log.e(TAG, "Database corruption detected in open()", ex);
                onCorruption();
                openInner();
            } else {
                throw ex;
            }
        }
    } catch (SQLiteException ex) {
        Log.e(TAG, "Failed to open database '" + getLabel() + "'.", ex);
        close();
        throw ex;
    }
}

private void openInner() {
    synchronized (mLock) {
        assert mCOnnectionPoolLocked== null;
        // 打开连接池,连接池内部首先打开一个连接,这个连接被定义为数据库主连接
        mCOnnectionPoolLocked= SQLiteConnectionPool.open(mConfigurationLocked);
        mCloseGuardLocked.open("close");
    }

    synchronized (sActiveDatabases) {
        sActiveDatabases.put(this, null);
    }
}

SQLiteClosable

  SQLiteClosable是 Android 应用框架层专门为数据库资源释放设计的类,类似 Java 为 I/O 提供的Closeable标准接口。 在android.database.sqlite包底下的类有着大量应用,确保资源能够及时释放。
  分析源码,其主要提供了两个接口:acquireReference(获取引用计数)和releaseReference(释放引用计数)(注意:这两个的调用总是成对出现)。当引用计数为0时,释放所有相关资源连接,而当引用计数已经为0,如果再次获取引用计数,会抛出IllegalStateException异常。

protected abstract void onAllReferencesReleased();

public void acquireReference() {
    synchronized(this) {
        if (mReferenceCount <= 0) {
            throw new IllegalStateException(
                    "attempt to re-open an already-closed object: " + this);
        }
        mReferenceCount++;
    }
}

public void releaseReference() {
    boolean refCountIsZero = false;
    synchronized(this) {
        refCountIsZero = --mReferenceCount == 0;
    }
    if (refCountIsZero) {
        onAllReferencesReleased();
    }
}

   // TODO 分析 IllegalStateException 异常出现的原因

SQLiteSession

  SQLiteDatabase 的每一个操作都需要通过 SQLiteSession 来完成。每个线程对于一个数据库最多持有一个 SQLiteSession,技术上通过ThreadLocal来保证。这个限制确保一个线程对于给定的数据库同一时刻不能使用多个数据库连接,保证了单进程内数据库使用不会产生死锁。关于事务管理,SQLiteSession 提供 SQLite 三种事务模式的支持,并且支持事务的嵌套。在分析 SQLiteSession 发挥的作用时,我们需要事先了解一下 SQLite 中的锁和事务机制。

SQLite的锁机制

  SQLite采用粗放型的锁。当一个连接要写数据库,所有其它的连接都会被锁住,直到写连接结束了它的事务。SQLite 有一个加锁表,来帮助不同的写数据库都能够在最后一刻再加锁,以保证最大的并发性。SQLite 使用锁逐步上升机制,为了写数据库,连接需要逐级地获得排它锁。SQLite 有5个不同的锁状态,每个数据库连接在同一时刻只能处于其中一个状态。
| 锁状态 | 锁描述 |
| ---------------- | ------------------------------------------------------------ |
| 未加锁-UNLOCKED | 数据库此时不被读取和写入,其他线程或进程可以在它们的锁状态允许的情况下读取或写入数据库。 |
| 共享锁-SHARED | 数据库可以被读取但不能被写入。同一时刻可以有多个线程或进程获得 SHARED 锁,因此同时可以有多个读者。当有一个或多个共享锁时,不允许其他线程或进程写入数据库文件。 |
| 保留锁-RESERVED | RESERVED 锁意味着进程准备写入数据库文件,但它目前只是从文件中读取。一次只能有一个 RESERVED 锁,多个 SHARED 锁可以与一个 RESERVED 锁共存。 RESERVED 与 PENDING 的不同之处在于,当存在 RESERVED 锁时,可以获取新的 SHARED 锁。 |
| 未觉锁-PENDING | PENDING 锁意味着持有锁的进程想要尽快写入数据库,并且只是等待释放当前所有的 SHARED 锁,以便它可以获得 EXCLUSIVE 锁。如果 PENDING 锁处于活动状态,则不允许获得新 SHARED 锁,但允许继续使用现有的 SHARED 锁。 |
| 排它锁-EXCLUSIVE | 写入数据库时需要持有 EXCLUSIVE 锁,同一时刻只允许只有一个 EXCLUSIVE 锁,并且不允许其他锁与它共存。为了最大限度地提高并发性,SQLite 应尽量减少持有独占锁的时间。 |

SQLite的事务类型

  SQLite 支持多个数据库连接同时发起读事务,但写事务同时只能有一个。读事务仅用于读取,写事务则允许读取和写入。读事务由 SELECT 语句启动,写入事务由 CREATE、DELETE、DROP、INSERT 或 UPDATE 等语句启动(统称为“写入语句”)。SQLite 提供了三种不同的事务类型,它们以不同的锁状态启动事务类型,这三种事务类型为:DEFERRED、IMMEDIATE、EXCLUSIVE,默认的事务类型是DEFERRED。事务在 BEGIN 类型中指定:

BEGIN [ DEFERRED | IMMEDIATE | EXCLUSIVE ] TRANSACTION;

BEGIN DEFERRED开启的事务不获取任何锁,直到它需要锁的时候。BEGIN 语句本身不会做什么事情,它开始于 UNLOCK 状态。默认情况下如果仅仅用 BEGIN 开始一个事务,那么事务就是 DEFERRED 的,同时它不会获取任何锁。当对数据库进行第一次读操作时,它会获取 SHARED 锁。同样,当进行第一次写操作时,它会获取 RESERVED 锁。

BEGIN IMMEDIATE 开启的事务会尝试获取 RESERVED 锁。如果成功,BEGIN IMMEDIATE保证没有别的连接可以写数据库。但是别的连接可以对数据库进行读操作。但是 RESERVED 锁会阻止其它连接的BEGIN IMMEDIATE或者BEGIN EXCLUSIVE命令。当其它连接执行上述命令时,会返回SQLITE_BUSY错误。当执行 COMMIT 操作时,如果返回SQLITE_BUSY错误,这意味着还有其它的读事务没有完成,得等它们执行完后才能提交事务。

BEGIN EXCLUSIVE开启的事务会试着获取对数据库的 EXCLUSIVE 锁。这与BEGIN IMMEDIATE类似,但是一旦成功,EXCLUSIVE 事务保证没有其它的连接,所以就可对数据库进行读写操作了。EXCLUSIVE 和 IMMEDIATE 在 WAL 模式下是一样的,但在其他日志模式下,EXCLUSIVE 会阻止其他数据库连接在事务进行时读取数据库。

事务的开启与结束

  上面两个小节介绍了 SQLite 中的锁机制和事务相关概念,现在来看一下 Android 应用框架层是如何进行封装的。
  SQLiteDatabase 提供了几个开启事务的方法,这几个方法主要区别在于传入的参数不同,最终实际调用public void beginTransaction(SQLiteTransactionListener transactionListener, boolean exclusive)方法。在这个方法中尤为重要的是exclusive参数,其决定了开启的事务类型,当为 true 时调用BEGIN EXCLUSIVE; 否则执行BEGIN IMMEDIATE

public void beginTransaction(); // 实际调用 beginTransaction(null, true)

public void beginTransactionNonExclusive(); // 实际调用 beginTransaction(null, false)

public void beginTransactionWithListener(SQLiteTransactionListener transactionListener);    // 实际调用 beginTransaction(transactionListener, true)

public void beginTransactionWithListenerNonExclusive(SQLiteTransactionListener transactionListener));   // 实际调用 beginTransaction(transactionListener, false)

public void beginTransaction(SQLiteTransactionListener transactionListener, boolean exclusive);

SQLiteDatabase 的一系列开启事务方法最终走到SQLiteSession#beginTransactionUnchecked。通过栈的数据结构保存嵌套事务的关系,嵌套事务的执行都在同一个数据库连接当中。如果任何嵌套事务执行失败,那么当最外层事务结束时,包括其所有嵌套事务在内的整个事务将被回滚。

private void beginTransactionUnchecked(int transactionMode,
        SQLiteTransactionListener transactionListener, int connectionFlags,
        CancellationSignal cancellationSignal) {
    if (cancellationSignal != null) {
        cancellationSignal.throwIfCanceled();
    }

    // 首次执行事务需要先获得一个数据库连接,当有嵌套事务时,最顶层事务获得一次数据库连接,嵌套的事务都使用这个连接对数据库进行操作
    if (mTransactiOnStack== null) {
        acquireConnection(null, connectionFlags, cancellationSignal); // might throw
    }
    try {
        // transactionMode在上层调用 SQLiteDatabase#beginTransaction(SQLiteTransactionListener transactionListener, boolean exclusive)时确定,
        // transactiOnMode= exclusive ? SQLiteSession.TRANSACTION_MODE_EXCLUSIVE :SQLiteSession.TRANSACTION_MODE_IMMEDIATE
        if (mTransactiOnStack== null) {
            // Execute SQL might throw a runtime exception.
            switch (transactionMode) {
                case TRANSACTION_MODE_IMMEDIATE:
                    mConnection.execute("BEGIN IMMEDIATE;", null,
                            cancellationSignal); // might throw
                    break;
                case TRANSACTION_MODE_EXCLUSIVE:
                    mConnection.execute("BEGIN EXCLUSIVE;", null,
                            cancellationSignal); // might throw
                    break;
                default:
                    mConnection.execute("BEGIN;", null, cancellationSignal); // might throw
                    break;
            }
        }

        // Listener might throw a runtime exception.
        if (transactionListener != null) {
            try {
                transactionListener.onBegin(); // might throw
            } catch (RuntimeException ex) {
                if (mTransactiOnStack== null) {
                    mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
                }
                throw ex;
            }
        }

        // 从对象池中复用对象,对象池为链表,每次从队尾取得无用的对象
        Transaction transaction = obtainTransaction(transactionMode, transactionListener);
        // 通过栈的方式存储事务,通过这种形式来对嵌套事务进行支持,栈顶为最内层的事务
        transaction.mParent = mTransactionStack;
        mTransactiOnStack= transaction;
    } finally {
        if (mTransactiOnStack== null) {
            releaseConnection(); // might throw
        }
    }
}

private void endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding) {
    if (cancellationSignal != null) {
        cancellationSignal.throwIfCanceled();
    }

    // 获取栈顶的事务
    final Transaction top = mTransactionStack;

    // 事务被标记成功或暂时让步且其下级事务没有失败时认为成功,yielding 为true的case 为 yieldTransaction 的调用
    boolean successful = (top.mMarkedSuccessful || yielding) && !top.mChildFailed;

    RuntimeException listenerException = null;
    final SQLiteTransactionListener listener = top.mListener;
    if (listener != null) {
        try {
            if (successful) {
                listener.onCommit(); // might throw
            } else {
                listener.onRollback(); // might throw
            }
        } catch (RuntimeException ex) {
            listenerException = ex;
            successful = false;
        }
    }

    // 当前栈顶的上一级事务
    mTransactiOnStack= top.mParent;
    // 将无用的事务对象缓存进对象池中
    recycleTransaction(top);

    // 判断事务栈中所有的事务是否执行完成
    if (mTransactionStack != null) {
        if (!successful) {
            // 标记下级事务失败
            mTransactionStack.mChildFailed = true;
        }
    } else {
        // 已经是栈底事务,整个事务只要有一个失败,那么会导致整个事务全部失败
        try {
            // 提交本次事务的修改
            if (successful) {
                mConnection.execute("COMMIT;", null, cancellationSignal); // might throw
            } else {
                // 回滚本次事务的修改
                mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
            }
        } finally {
            releaseConnection(); // might throw
        }
    }

    if (listenerException != null) {
        throw listenerException;
    }
}

  现在我们来看一下beginTransactionUnchecked方法中connectionFlags的确定。由SQLiteDatabase#beginTransaction(SQLiteTransactionListener transactionListener, boolean exclusive)的调用会走入下面的代码,connectionFlags会被标记为需要获取主连接。因此,显式的在代码里面调用beginTransaction方法,即使执行的是读语句也会获取主连接,而同时只能获取一个主连接,因此会导致性能下降。所以如果读语句没有必要,无需显式调用事务开启方法。

@UnsupportedAppUsage
private void beginTransaction(SQLiteTransactionListener transactionListener,
        boolean exclusive) {
    acquireReference();
    try {
        getThreadSession().beginTransaction(
                exclusive ? SQLiteSession.TRANSACTION_MODE_EXCLUSIVE :
                        SQLiteSession.TRANSACTION_MODE_IMMEDIATE,
                transactionListener,
                getThreadDefaultConnectionFlags(false /*readOnly*/), null);
    } finally {
        releaseReference();
    }
}

int getThreadDefaultConnectionFlags(boolean readOnly) {
    int flags = readOnly ? SQLiteConnectionPool.CONNECTION_FLAG_READ_ONLY :
            SQLiteConnectionPool.CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY;
    if (isMainThread()) {
        flags |= SQLiteConnectionPool.CONNECTION_FLAG_INTERACTIVE;
    }
    return flags;
}

SQLiteConnectionPool

  SQLiteConnectionPool 主要用于缓存数据库连接,其包含一个主连接(拥有读写能力)和若干非主连接(拥有只读能力)。

连接池的创建

// 在构造器中确定连接池的大小
private SQLiteConnectionPool(SQLiteDatabaseConfiguration configuration) {
    mCOnfiguration= new SQLiteDatabaseConfiguration(configuration);
    setMaxConnectionPoolSizeLocked();
    // If timeout is set, setup idle connection handler
    // In case of MAX_VALUE - idle connections are never closed
    if (mConfiguration.idleConnectionTimeoutMs != Long.MAX_VALUE) {
        setupIdleConnectionHandler(Looper.getMainLooper(),
                mConfiguration.idleConnectionTimeoutMs);
    }
}

// 对于非内存数据库且开启了 WAL 日志模式的数据库,其池大小由内建的配置决定,一般为4个,取决于不同机型内部的设置,包含1个主连接和 poolSize - 1的非主连接。
// 通过源码也可以看出,连接池大小最小为2
private void setMaxConnectionPoolSizeLocked() {
    if (!mConfiguration.isInMemoryDb()
            && (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
        mMaxCOnnectionPoolSize= SQLiteGlobal.getWALConnectionPoolSize();
    } else {
        // We don't actually need to always restrict the connection pool size to 1
        // for non-WAL databases.  There might be reasons to use connection pooling
        // with other journal modes. However, we should always keep pool size of 1 for in-memory
        // databases since every :memory: db is separate from another.
        // For now, enabling connection pooling and using WAL are the same thing in the API.
        mMaxCOnnectionPoolSize= 1;
    }
}

public static int getWALConnectionPoolSize() {
    int value = SystemProperties.getInt("debug.sqlite.wal.poolsize",
            Resources.getSystem().getInteger(
            com.android.internal.R.integer.db_connection_pool_size));
    return Math.max(2, value);
}

// 连接池中主要打开一个数据库主连接
private void open() {
    // Open the primary connection.
    // This might throw if the database is corrupt.
    mAvailablePrimaryCOnnection= openConnectionLocked(mConfiguration,
            true /*primaryConnection*/); // might throw
    // Mark it released so it can be closed after idle timeout
    synchronized (mLock) {
        if (mIdleConnectionHandler != null) {
            mIdleConnectionHandler.connectionReleased(mAvailablePrimaryConnection);
        }
    }

    // Mark the pool as being open for business.
    mIsOpen = true;
    mCloseGuard.open("close");
}

获取连接

  SQLiteSession 对数据库进行操作前需要获得一个数据库连接,其最终调用SQLiteConnectionPool#acquireConnection来取得一个数据库连接。

// SQLiteDatabase#getThreadDefaultConnectionFlags
int getThreadDefaultConnectionFlags(boolean readOnly) {
    int flags = readOnly ? SQLiteConnectionPool.CONNECTION_FLAG_READ_ONLY :
            SQLiteConnectionPool.CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY;
    if (isMainThread()) {
        flags |= SQLiteConnectionPool.CONNECTION_FLAG_INTERACTIVE;
    }
    return flags;
}

// connectionFlags 表示连接类型,其通过 SQLiteDatabase#getThreadDefaultConnectionFlags 确定
public SQLiteConnection acquireConnection(String sql, int connectionFlags,
        CancellationSignal cancellationSignal) {
    SQLiteConnection con = waitForConnection(sql, connectionFlags, cancellationSignal);
    synchronized (mLock) {
        if (mIdleConnectionHandler != null) {
            mIdleConnectionHandler.connectionAcquired(con);
        }
    }
    return con;
}

// 等待可用的连接
private SQLiteConnection waitForConnection(String sql, int connectionFlags,
        CancellationSignal cancellationSignal) {
    // 通过 connectionFlags 确定是否是主连接,当需要写能力时 wantPrimaryConnection 为true
    final boolean wantPrimaryCOnnection=
            (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) {
            cOnnection= tryAcquireNonPrimaryConnectionLocked(
                    sql, connectionFlags); // might throw
        }
        // 没有可用的非主连接或需要主连接,尝试获取主连接
        if (cOnnection== null) {
            cOnnection= tryAcquirePrimaryConnectionLocked(connectionFlags); // might throw
        }
        if (connection != null) {
            return connection;
        }

        // 没有可用的主连接,根据连接优先级插入队列,如果是在主线程获取连接,则为高优先级
        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;
    }

    // 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 {
        // 默认30s
        long busyTimeoutMillis = CONNECTION_POOL_BUSY_MILLIS;
        long nextBusyTimeoutTime = waiter.mStartTime + busyTimeoutMillis;
        for (;;) {
            // 检测处于泄漏的连接并使用它
            if (mConnectionLeaked.compareAndSet(true, false)) {
                synchronized (mLock) {
                    // 唤醒等待
                    wakeConnectionWaitersLocked();
                }
            }

            // 使当前线程进入休眠,最长30s
            LockSupport.parkNanos(this, busyTimeoutMillis * 1000000L);

            // Clear the interrupted flag, just in case.
            Thread.interrupted();

            // Check whether we are done waiting yet.
            synchronized (mLock) {
                throwIfClosedLocked();

                final SQLiteConnection cOnnection= waiter.mAssignedConnection;
                final RuntimeException ex = waiter.mException;
                // 释放的可用连接是否给到当前这个等待的 waiter,如果是则直接返回
                if (connection != null || ex != null) {
                    recycleConnectionWaiterLocked(waiter);
                    if (connection != null) {
                        return connection;
                    }
                    throw ex; // rethrow!
                }

                // 没有获得可用连接,从新调整休眠时间
                final long now = SystemClock.uptimeMillis();
                if (now 

释放连接

public void releaseConnection(SQLiteConnection connection) {
    synchronized (mLock) {
        if (mIdleConnectionHandler != null) {
            mIdleConnectionHandler.connectionReleased(connection);
        }
        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();
        }
    }
}

参考链接

  • SQLite Locking
  • SQLite Transaction
  • Writing-Ahead Logging

推荐阅读
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
author-avatar
手机用户2602922857
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有