Android 应用框架层 SQLite 源码分析
概述
Android 在应用框架层为开发者提供了 SQLite 相关操作接口,其归属于android.database.sqlite
包底下,主要包含SQLiteProgram
, SQLiteDatabase
, SQLiteSession
, SQLiteConnectionPool
和SQLiteConnection
等一些类。相比较 SQLite 提供的轻量级接口,应用框架层为开发者封装的 SQLite 抽象层显得更为复杂,但也为开发者屏蔽了更多细节,减小 SQLite 使用难度。
在设计上,一个 SQLiteDatabase 持有一个 SQLiteConnectionPool, SQLiteConnectionPool 包含 n 个 SQLiteConnection,其根据日志模式的不同,连接池容量也不同。每个线程对于 SQLiteDatabase 的操作通过ThreadLocal
创建的 SQLiteSession 来进行管理,而 SQLiteSession 进行操作时需要预先获得一个 SQLiteConnection。如果此时数据库连接池中的连接都被使用,那么会阻塞直到获得可用连接。
SQLiteDatabase
SQLiteDatabase 提供了一系列管理数据库的方法,通过它我们可以进行增删改查和执行 SQLite 命令语句等操作。其高度封装了SQLiteSession
, SQLiteConnectionPool
和SQLiteConnection
的执行细节,开发者仅需关心上层 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
采用延迟初始化的方式来创建或打开数据库,开发者调用getReadableDatabase
或getWritableDatabase
取得一个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 < mMinimumSupportedVersion) {
File databaseFile = new File(db.getPath());
onBeforeDelete(db);
db.close();
if (SQLiteDatabase.deleteDatabase(databaseFile)) {
mIsInitializing = false;
return getDatabaseLocked(writable);
} else {
throw new IllegalStateException("Unable to delete obsolete database "
+ mName + " with version " + version);
}
} else {
// 开启事务,创建db或升降级
db.beginTransaction();
try {
// 第一次创建数据库 version 默认为0
if (version == 0) {
onCreate(db);
} else {
if (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 < nextBusyTimeoutTime) {
busyTimeoutMillis = now - nextBusyTimeoutTime;
} else {
logConnectionPoolBusyLocked(now - waiter.mStartTime, connectionFlags);
busyTimeoutMillis = CONNECTION_POOL_BUSY_MILLIS;
nextBusyTimeoutTime = now + busyTimeoutMillis;
}
}
}
} finally {
// Remove the cancellation listener.
if (cancellationSignal != null) {
cancellationSignal.setOnCancelListener(null);
}
}
}
释放连接
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();
}
}
}
参考链接
Android 应用框架层 SQLite 源码分析的更多相关文章
- DotNetty网络通信框架学习之源码分析
DotNetty网络通信框架学习之源码分析 有关DotNetty框架,网上的详细资料不是很多,有不多的几个博友做了简单的介绍,也没有做深入的探究,我也根据源码中提供的demo做一下记录,方便后期查阅. ...
- Android View事件分发-从源码分析
View事件分发-从源码分析 学习自 <Android开发艺术探索> https://blog.csdn.net/qian520ao/article/details/78555397?lo ...
- 【Android】Handler、Looper源码分析
一.前言 源码分析使用的版本是 4.4.2_r1. Handler和Looper的入门知识以及讲解可以参考我的另外一篇博客:Android Handler机制 简单而言:Handler和Looper是 ...
- Android服务之PackageManagerService启动源码分析
了解了Android系统的启动过程的读者应该知道,Android的所有Java服务都是通过SystemServer进程启动的,并且驻留在SystemServer进程中.SystemServer进程在启 ...
- 深入理解分布式调度框架TBSchedule及源码分析
简介 由于最近工作比较忙,前前后后花了两个月的时间把TBSchedule的源码翻了个底朝天.关于TBSchedule的使用,网上也有很多参考资料,这里不做过多的阐述.本文着重介绍TBSchedule的 ...
- $Django cbv源码分析 djangorestframework框架之APIView源码分析
1 CBV的源码分析 #视图 class login (View): pass #路由 url(r'^books/$', views.login.as_view()) #阅读源码: #左侧工程栏--- ...
- Android线程间异步通信机制源码分析
本文首先从整体架构分析了Android整个线程间消息传递机制,然后从源码角度介绍了各个组件的作用和完成的任务.文中并未对基础概念进行介绍,关于threadLacal和垃圾回收等等机制请自行研究. 基础 ...
- Android JobService的使用及源码分析
Google在Android 5.0中引入JobScheduler来执行一些需要满足特定条件但不紧急的后台任务,APP利用JobScheduler来执行这些特殊的后台任务时来减少电量的消耗.本文首先介 ...
- 设计模式(十五)——命令模式(Spring框架的JdbcTemplate源码分析)
1 智能生活项目需求 看一个具体的需求 1) 我们买了一套智能家电,有照明灯.风扇.冰箱.洗衣机,我们只要在手机上安装 app 就可以控制对这些家电工作. 2) 这些智能家电来自不同的厂家,我们不想针 ...
随机推荐
- dotnet 替换 ASP.NET Core 的底层通讯为命名管道的 IPC 库
这是一个用于本机多进程进行 IPC 通讯的库,此库的顶层 API 是采用 ASP.NET Core 的 MVC 框架,其底层通讯不是传统的走网络的方式,而是通过 dotnetCampus.Ipc 开源 ...
- MySQL 事务的隔离级别及锁操作的一点点演示
MySQL 版本:5.7 安装环境:MAC OS 一.测试数据 测试数据库:test:测试表:tt CREATE TABLE `tt` ( `id` int(11) DEFAULT NULL, `na ...
- 新手应该如何学习 PHP 语言?
其实php开发,不只是一个简单的php开发,而是整个一个行业,一般叫web开发,或者php后端开发,所以从html,css,js,jq,php,sql基本这些都要有了解.当然你有html,css基础, ...
- Linux开机显示模式切换
修改vim /etc/inittab 默认为5-图形界面模式,改为3-多用户模式即可 # Default runlevel. The runlevels used are: # 0 - halt (D ...
- hibernate中的一级缓存与闪照区
首先Hibernate中的一级缓存默认是打开的,并且范围从session创建到session关闭,存储的数据必须是持久态的数据. 1 //从session创建开始,一级缓存也跟着创建 2 Sessio ...
- 解决sublime代码不提示的问题
如果想让sublime在你输入标签的过程中给你提示,需要按要求开启以下功能. 1.开启代码自动提示功能
- LaunchScreen&LaunchImage
优先级:LaunchScreen > LaunchImage 在xcode配置了,不起作用 1.清空xcode缓存 2.直接删掉程序 重新运行 如果是通过LaunchImage设置启动界面,那么 ...
- 匿名内部类不能访问外部类方法中的局部变量,除非变量被声明为final类型
1. 这里所说的"匿名内部类"主要是指在其外部类的成员方法内定义,同时完成实例化的类,若其访问该成员方法中的局部变量,局部变量必须要被final修饰.2. 原因是编译程序实现上的困 ...
- JS快速入门(一)
目录 Javascript快速入门(一) 变量的命名规则 变量与常量 变量 常量 数据类型 数值型:Number 字符串:String 常用方法: 布尔类型:Boolean 未定义类型 :Undefi ...
- Elasticsearch使用系列-.NET6对接Elasticsearch
Elasticsearch使用系列-ES简介和环境搭建 Elasticsearch使用系列-ES增删查改基本操作+ik分词 Elasticsearch使用系列-基本查询和聚合查询+sql插件 Elas ...