概述
ContentProvider用于进程间共享数据,通常使用方法只要我们继承自ContentProvider并重写其中的onCreate,query,insert、update等方法就可使用ContentProvider来进行进程间数据共享,但是,知道怎么使用还是不够的,还需知道它的实现原理。本文以上层应用中调用ContentResolver.query()方法开始,到native层调用数据库相关API获取数据并返回为例进行ContentProvider的执行过程和数据共享原理进行分析。
1.获取ContentResolver
在我们的程序中,调用getContentResolver()
就能获得一个ContentResolver
对象。我们就一次为开始,进行分析。
getContentResolver
一般在我们的自定义的Activity
中调用。Activity
继承自ContextThemeWrapper
,ContextThemeWrapper
又继承自ContextWrapper
,getContentResolver
真正的发生地就是在ContextWrapper
中:
@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}
直接返回mBase.getContentResolver
,mBase
实际类型为ContextImpl
。
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
直接返回mContentResolver
。mContentResolver
在ContextImpl
构造函数中直接初始化为ApplicationContentResolver
:
mContentResolver = new ApplicationContentResolver(this, mainThread);
至此,利用上下文创建了一个ContentResolver对象,并获得返回。
2.利用ContentResolver对象调用query方法
getContentResolver()
返回了一个ContentResolver
对象,调用query
方法:
@Override
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal) {
Objects.requireNonNull(uri, "uri");
try {
if (mWrapped != null) {
return mWrapped.query(uri, projection, queryArgs, cancellationSignal);
}
} catch (RemoteException e) {
return null;
}
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
long startTime = SystemClock.uptimeMillis();
ICancellationSignal remoteCancellationSignal = null;
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
remoteCancellationSignal = unstableProvider.createCancellationSignal();
cancellationSignal.setRemote(remoteCancellationSignal);
}
try {
qCursor = unstableProvider.query(mPackageName, mAttributionTag, uri, projection,
queryArgs, remoteCancellationSignal);
} catch (DeadObjectException e) {
// The remote process has died... but we only hold an unstable
// reference though, so we might recover!!! Let's try!!!!
// This is exciting!!1!!1!!!!1
unstableProviderDied(unstableProvider);
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
return null;
}
qCursor = stableProvider.query(mPackageName, mAttributionTag, uri, projection,
queryArgs, remoteCancellationSignal);
}
if (qCursor == null) {
return null;
}
ContentResolver
有多个query
重载方法,最终都会调用到该方法内,主要语句在928行:利用acquireUnstableProvider
获取一个IContentProvider
对象。利用该IContentProvider
对象,调用query方法,IContentProvider
是一个IBinder对象,能通过它访问ContentProvider
中的服务。此处就是我们即将访问的ContentProvider
,即Uri对象的ContentProvider
。
2.1 acquireUnstableProvider方法的执行
通过前面知道,当前方法所属的对象的实际类型为ApplicationContentResolver
,在该类中重写了acquireUnstableProvider
方法:
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
mMainThread对象为一个ActivityThread类型对象,ActivityThread类管理整个程序的运行,具体执行过程下篇文章分析,本章先到此,知道这一句执行结束,能获得一个IContentProvider即可。
3.IContentProvider.query执行过程
3.0 Cursor继承关系
在继续之前,先来了解一下Cursor类,以及它的继承关系,因为后面的查询过程中,和每个Cursor类息息相关。
3.1 query执行过程
IContentProvider
为一个IBinder对象,本地代理类为ContentProviderProxy,该类为ContentProviderNative
的内部类,所以query
的执行地为ContentProviderNative.ContentProviderProxy
内:
@Override
public Cursor query(@NonNull AttributionSource attributionSource, Uri url,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable ICancellationSignal cancellationSignal)
throws RemoteException {
BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
data.writeInterfaceToken(IContentProvider.descriptor);
attributionSource.writeToParcel(data, 0);
url.writeToParcel(data, 0);
int length = 0;
if (projection != null) {
length = projection.length;
}
data.writeInt(length);
for (int i = 0; i < length; i++) {
data.writeString(projection[i]);
}
data.writeBundle(queryArgs);
data.writeStrongBinder(adaptor.getObserver().asBinder());
data.writeStrongBinder(
cancellationSignal != null ? cancellationSignal.asBinder() : null);
mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
DatabaseUtils.readExceptionFromParcel(reply);
if (reply.readInt() != 0) {
BulkCursorDescriptor d = BulkCursorDescriptor.CREATOR.createFromParcel(reply);
Binder.copyAllowBlocking(mRemote, (d.cursor != null) ? d.cursor.asBinder() : null);
adaptor.initialize(d);
} else {
adaptor.close();
adaptor = null;
}
return adaptor;
} catch (RemoteException ex) {
adaptor.close();
throw ex;
} catch (RuntimeException ex) {
adaptor.close();
throw ex;
} finally {
data.recycle();
reply.recycle();
}
}
首先先新建了一个BulkCursorToCursorAdaptor
对象,该类继承自Cursor,所以其实最终我们在getContentResolver().query(...)
中返回的Cursor实际类型为BulkCursorToCursorAdaptor
、而后新建Pacel对象并将参数写入data中,准备进行进程间通信。mRemote为远程ContentProvider的IBinder本地对象,执行这条语句后,接下来过程在远程执行。中间发生了什么,后面讨论,我们先一口气了解完大的过程。当远程执行时,当前线程阻塞,直至远程执行结束。远程执行结束,结果写入reply中。利用reply中数据,新建一个BulkCursorDescription
对象,利用该对象初始化BulkCursorToCursorAdaptor
对象,接着返回该adaptor
对象,一次ContentProvider
查询结束。
3.2 mRemote.transact发生了啥
mRemote.transact
执行后,进行一次进程间通信码为IContentProvider.QEURY_TRANSACTION
的请求。在ContentProvider
端,处理请求的类为ContentProviderNative
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
try {
switch (code) {
case QUERY_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
AttributionSource attributionSource = AttributionSource.CREATOR
.createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
// String[] projection
int num = data.readInt();
String[] projection = null;
if (num > 0) {
projection = new String[num];
for (int i = 0; i < num; i++) {
projection[i] = data.readString();
}
}
Bundle queryArgs = data.readBundle();
IContentObserver observer = IContentObserver.Stub.asInterface(
data.readStrongBinder());
ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
Cursor cursor = query(attributionSource, url, projection, queryArgs,
cancellationSignal);
if (cursor != null) {
CursorToBulkCursorAdaptor adaptor = null;
try {
adaptor = new CursorToBulkCursorAdaptor(cursor, observer,
getProviderName());
cursor = null;
BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();
adaptor = null;
reply.writeNoException();
reply.writeInt(1);
d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} finally {
// Close cursor if an exception was thrown while constructing the adaptor.
if (adaptor != null) {
adaptor.close();
}
if (cursor != null) {
cursor.close();
}
}
从data中取出请求者写入的参数,调用query
方法获得一个Cursor,此处Cursor实际类型为SQLiteCursor
(后面说具体情况)。利用该Cursor对象新建一个CursorToBulkCursorAdaptor
对象,接着利用该CursorToBulkCursorAdaptor
对象获取一个BulkCursorDescriptor
对象(就是之前从reply中取出来的那货)。将该对象写入reply中。返回reply。此处的问题是:query中发生了啥?adaptor.getBulkCursorDescriptor()
中发生了啥?
3.3 query执行过程
此处的qeury调用过程由ContentProvider的内部类Transport类处理:
重点在圈出来的代码中,调用mInterface
的query
方法,mInterface
声明类型为ContentInterface
,实际类型为ContentProvider.this
所以此处直接调用到我们自定义的ContentProvider
中,在自定义ContentProvider
中,query
有多个继承而来的重载形式,但是最终都会调用到我们自定义的query
方法体内。在我们的实现的query
方法中,肯定会有一句:SQLiteDataBase db;db.query(...)
。继续深入,db.query
:
public Cursor query(boolean distinct, String table, String[] columns,
String selection, String[] selectionArgs, String groupBy,
String having, String orderBy, String limit) {
return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,
groupBy, having, orderBy, limit, null);
}
在SQLiteDataBase实现中,query有多个实现方法,queryWithFactory也有两个实现方法,但是最终都会调用同一个queryWithFactory:
public Cursor queryWithFactory(CursorFactory cursorFactory,
boolean distinct, String table, String[] columns,
String selection, String[] selectionArgs, String groupBy,
String having, String orderBy, String limit, CancellationSignal cancellationSignal) {
acquireReference();
try {
String sql = SQLiteQueryBuilder.buildQueryString(
distinct, table, columns, selection, groupBy, having, orderBy, limit);
return rawQueryWithFactory(cursorFactory, sql, selectionArgs,
findEditTable(table), cancellationSignal);
} finally {
releaseReference();
}
}
利用SQLiteQueryBuilder
类的静态方法buildQueryString
方法,将我们以非sql方法查询的查询转为sql,具体过程不再讨论,无非就是根据每个字段,新建一个StringBuilder
,往里面不停地加调价、加字段。接着调用rawQueryWithFactory
:
public Cursor rawQueryWithFactory(
CursorFactory cursorFactory, String sql, String[] selectionArgs,
String editTable, CancellationSignal cancellationSignal) {
acquireReference();
try {
SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,
cancellationSignal);
return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory,
selectionArgs);
} finally {
releaseReference();
}
}
(参数对不上?没关系,因为中间有一个重载)新建了一个SQLiteCursorDriver,接着调用该对象query方法。先看下构造函数有没有做什么事:
public final class SQLiteDirectCursorDriver implements SQLiteCursorDriver {
private final SQLiteDatabase mDatabase;
private final String mEditTable;
private final String mSql;
private final CancellationSignal mCancellationSignal;
private SQLiteQuery mQuery;
public SQLiteDirectCursorDriver(SQLiteDatabase db, String sql, String editTable,
CancellationSignal cancellationSignal) {
mDatabase = db;
mEditTable = editTable;
mSql = sql;
mCancellationSignal = cancellationSignal;
}
完全没有,就是简单的赋值赋值再赋值。接下来看query方法:
public Cursor query(CursorFactory factory, String[] selectionArgs) {
final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancellationSignal);
final Cursor cursor;
try {
query.bindAllArgsAsStrings(selectionArgs);
if (factory == null) {
cursor = new SQLiteCursor(this, mEditTable, query);
} else {
cursor = factory.newCursor(mDatabase, this, mEditTable, query);
}
} catch (RuntimeException ex) {
query.close();
throw ex;
}
mQuery = query;
return cursor;
}
新建了一个SQLiteQuery
对象,看一下父类的构造函数
SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs,
CancellationSignal cancellationSignalForPrepare) {
mDatabase = db;
mSql = sql.trim();
int n = DatabaseUtils.getSqlStatementType(mSql);
switch (n) {
case DatabaseUtils.STATEMENT_BEGIN:
case DatabaseUtils.STATEMENT_COMMIT:
case DatabaseUtils.STATEMENT_ABORT:
mReadOnly = false;
mColumnNames = EMPTY_STRING_ARRAY;
mNumParameters = 0;
break;
default:
boolean assumeReadOnly = (n == DatabaseUtils.STATEMENT_SELECT);
SQLiteStatementInfo info = new SQLiteStatementInfo();
db.getThreadSession().prepare(mSql,
db.getThreadDefaultConnectionFlags(assumeReadOnly),
cancellationSignalForPrepare, info);
mReadOnly = info.readOnly;
mColumnNames = info.columnNames;
mNumParameters = info.numParameters;
break;
}
if (bindArgs != null && bindArgs.length > mNumParameters) {
throw new IllegalArgumentException("Too many bind arguments. "
+ bindArgs.length + " arguments were provided but the statement needs "
+ mNumParameters + " arguments.");
}
if (mNumParameters != 0) {
mBindArgs = new Object[mNumParameters];
if (bindArgs != null) {
System.arraycopy(bindArgs, 0, mBindArgs, 0, bindArgs.length);
}
} else {
mBindArgs = null;
}
}
(我嘞个乖乖,构造函数这么多,一看就不简单)看case就可知道什么意义,query肯定是进default
了。重点在19行,获取一个ThreadSession
,接着调用prepare方法。该方法的作用就是,再深入一层到native层,调用sqlite3_prepare
方法,进行sql语句编译,将sql语句编译成statement
,一种sqlite数据库引擎能执行的sqlite程序。在prepare调用中,还传入了一个SQLiteStatementInfo对象,接着23行mColumnNames = info
。columnNames
获取了当前查询的表的列名集合。(后面有一处直接就获取列名了,为什么还没查数据库就获取列名了,列名不为空,原因在此)。这儿不再继续挖掘,因为查询数据库的时候会说,和这儿一毛一样,就是调用的方法不同。ok,现在sql已经编译好了。原路返回到上一步的query
方法中。
public Cursor query(CursorFactory factory, String[] selectionArgs) {
final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancellationSignal);
final Cursor cursor;
try {
query.bindAllArgsAsStrings(selectionArgs);
if (factory == null) {
cursor = new SQLiteCursor(this, mEditTable, query);
} else {
cursor = factory.newCursor(mDatabase, this, mEditTable, query);
}
} catch (RuntimeException ex) {
query.close();
throw ex;
}
mQuery = query;
return cursor;
}
接下来就是利用刚刚创建的SQLiteQuery
对象新建一个SQLiteCursor
:
public SQLiteCursor(SQLiteCursorDriver driver, String editTable, SQLiteQuery query) {
if (query == null) {
throw new IllegalArgumentException("query object cannot be null");
}
mDriver = driver;
mEditTable = editTable;
mColumnNameMap = null;
mQuery = query;
mColumns = query.getColumnNames();
}
就是赋值操作,同时获取了查询的列名集合。
接下来就是沿着调用栈一路返回到3.2节中的onTransact
函数中。
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
try {
switch (code) {
case QUERY_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
AttributionSource attributionSource = AttributionSource.CREATOR
.createFromParcel(data);
Uri url = Uri.CREATOR.createFromParcel(data);
// String[] projection
int num = data.readInt();
String[] projection = null;
if (num > 0) {
projection = new String[num];
for (int i = 0; i < num; i++) {
projection[i] = data.readString();
}
}
Bundle queryArgs = data.readBundle();
IContentObserver observer = IContentObserver.Stub.asInterface(
data.readStrongBinder());
ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
data.readStrongBinder());
Cursor cursor = query(attributionSource, url, projection, queryArgs,
cancellationSignal);
if (cursor != null) {
CursorToBulkCursorAdaptor adaptor = null;
try {
adaptor = new CursorToBulkCursorAdaptor(cursor, observer,
getProviderName());
cursor = null;
BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();
adaptor = null;
reply.writeNoException();
reply.writeInt(1);
d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} finally {
// Close cursor if an exception was thrown while constructing the adaptor.
if (adaptor != null) {
adaptor.close();
}
if (cursor != null) {
cursor.close();
}
}
现在我们知道了返回的Cursor
为什么实际类型为SQLiteCursor
,而且里面的一下重要数据也都清楚了。接着,利用该SQLiteCursor
对象新建一个CursorToBulkCursorAdapter
对象,构造函数也就是一些赋值操作,不再贴代码。重要的语句是40行,利用该adapter
对象获取一个BulkCursorDescriptor
。看到这儿了,你肯定有所疑问,onTransact
中query
调用后返回的Cursor
,好像里面并没有进行查询并将结果集写入Cursor
啊。没错,现在的query
返回的Cursor
对象只是一个数据库查询计划,它里面保存着一个将要进行查询的已经编译好的sqlite
程序、数据库连接等一系列进行数据库查询需要的数据。它第一次查询数据时是在从Cursor
对象中读取数据的时候。而这个的发生地就在adaptor.getBulkCursorDescriptor
中。
4.adaptor.getBulkCursorDescriptor执行分析
先上代码:
public BulkCursorDescriptor getBulkCursorDescriptor() {
synchronized (mLock) {
throwIfCursorIsClosed();
BulkCursorDescriptor d = new BulkCursorDescriptor();
d.cursor = this;
d.columnNames = mCursor.getColumnNames();
d.wantsAllOnMoveCalls = mCursor.getWantsAllOnMoveCalls();
d.count = mCursor.getCount();
d.window = mCursor.getWindow();
if (d.window != null) {
// Acquire a reference to the window because its reference count will be
// decremented when it is returned as part of the binder call reply parcel.
d.window.acquireReference();
}
return d;
}
}
简单粗暴,直接新建一个BulkCursorDescriptor
对象,接着从mCursor成员变量中获取数据,这个mCursor就是刚刚传入的SQLiteCursor
对象。第一步,获取列名集合:
public DragSourceCursor(String value) {
mValue = value;
}
直接返回mColumns成员变量,而mColumns赋值在前面的构造函数中,从SQLiteQuery对象中获取,SQLiteQuery的列名集合怎么来的?前文已经说过了。到这儿,还是没有进行数据库查询。
接着地9行,从mCursor中获取数据数:
public int getCount() {
if (mCount == NO_COUNT) {
fillWindow(0);
}
return mCount;
}
看到这个NO_COUNT了吗?这个常量的意义是,还没有进行数据库查询。那么就是还没进行数据库查询,就fillWindow,用什么fill,当然是数据库数据集了:
private void fillWindow(int requiredPos) {
clearOrCreateWindow(getDatabase().getPath());
try {
Preconditions.checkArgumentNonnegative(requiredPos,
"requiredPos cannot be negative");
if (mCount == NO_COUNT) {
mCount = mQuery.fillWindow(mWindow, requiredPos, requiredPos, true);
mCursorWindowCapacity = mWindow.getNumRows();
if (SQLiteDebug.NoPreloadHolder.DEBUG_SQL_LOG) {
Log.d(TAG, "received count(*) from native_fill_window: " + mCount);
}
} else {
int startPos = mFillWindowForwardOnly ? requiredPos : DatabaseUtils
.cursorPickFillWindowStartPosition(requiredPos, mCursorWindowCapacity);
mQuery.fillWindow(mWindow, startPos, requiredPos, false);
}
} catch (RuntimeException ex) {
// Close the cursor window if the query failed and therefore will
// not produce any results. This helps to avoid accidentally leaking
// the cursor window if the client does not correctly handle exceptions
// and fails to close the cursor.
closeWindow();
throw ex;
}
}
第一步,clearOrCreateWindow
,window是什么呢?window就是一块共享内存,用于进程间数据共享,在ContentProvider
端将数据写入一块共享内存中,同时将这块共享内存的地址放入返回结果中,在请求端中通过该地址就能访问到这块共享内存,从而实现了数据共享。你可能说,之前不是IBinder来进行数据传送了吗?直接发结果写入IBinder中不就完事了,这么麻烦干嘛?IBinder的确可以共享数据,但是当数据量大时,它就非常耗时,所以,选择用共享内存来进行数据传送,IBinder中只传送共享内存的句柄,从而能高效的传送数据。
接着看下clearOrCreateWindow
做了什么:这个方法在AbstractWindowCursor
中
@UnsupportedAppUsage
protected void clearOrCreateWindow(String name) {
if (mWindow == null) {
mWindow = new CursorWindow(name);
} else {
mWindow.clear();
}
}
简单明了,么有就创建,有就清空。而在创建CursorWindow
中,会调用native
层的CursorWindow
类的nativeCreate
函数,在native的CursorWindow
中会初始化一块共享内存块,最后将该对象返回到java层中的CursorWindow
,存于java层的CursorWindow
对象的成员变量中,因此就可以通过java层的CursorWindow
对象访问到该共享内存块。具体细节不再讨论,可以自行查阅源码。
进入到SQLiteQuery.fillWindow:
int fillWindow(CursorWindow window, int startPos, int requiredPos, boolean countAllRows) {
acquireReference();
try {
window.acquireReference();
try {
int numRows = getSession().executeForCursorWindow(getSql(), getBindArgs(),
window, startPos, requiredPos, countAllRows, getConnectionFlags(),
mCancellationSignal);
return numRows;
} catch (SQLiteDatabaseCorruptException ex) {
onCorruption();
throw ex;
} catch (SQLiteException ex) {
Log.e(TAG, "exception: " + ex.getMessage() + "; query: " + getSql());
throw ex;
} finally {
window.releaseReference();
}
} finally {
releaseReference();
}
}
关键语句6行:getSession()
函数,前面在SQLiteProgram中出现过,前面没有具体深入,在这儿深入,前面逻辑一样的。
这个方法位于SQLiteProgram
中,调用mDatabase
的成员函数getThreadSession
方法,mDatabases
类型为SQLiteDatabase
:
protected final SQLiteSession getSession() {
return mDatabase.getThreadSession();
}
mThreadSession
类型为:ThreadLocal<SQLiteSession>
,因此,get方法保证了每个线程中都能获取到自己独有的SQLiteSession
。回到上一步:getSession().execteForCursorWindow
:
public int executeForCursorWindow(String sql, Object[] bindArgs,
CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
int connectionFlags, CancellationSignal cancellationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
if (window == null) {
throw new IllegalArgumentException("window must not be null.");
}
if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
window.clear();
return 0;
}
acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
try {
return mConnection.executeForCursorWindow(sql, bindArgs,
window, startPos, requiredPos, countAllRows,
cancellationSignal); // might throw
} finally {
releaseConnection(); // might throw
}
}
首先判断是不是执行特殊sql语句:
private boolean executeSpecial(String sql, Object[] bindArgs, int connectionFlags,
CancellationSignal cancellationSignal) {
if (cancellationSignal != null) {
cancellationSignal.throwIfCanceled();
}
final int type = DatabaseUtils.getSqlStatementType(sql);
switch (type) {
case DatabaseUtils.STATEMENT_BEGIN:
beginTransaction(TRANSACTION_MODE_EXCLUSIVE, null, connectionFlags,
cancellationSignal);
return true;
case DatabaseUtils.STATEMENT_COMMIT:
setTransactionSuccessful();
endTransaction(cancellationSignal);
return true;
case DatabaseUtils.STATEMENT_ABORT:
endTransaction(cancellationSignal);
return true;
}
return false;
}
当sql是开启事务、提交事务、abort时,调用相应的处理函数。不再深入。回到上一步:acquireConnection:
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;
}
从连接池中获取一个连接,mConnectionPool类型为SQLiteConnection:
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;
}
直接调用waitForConnection
:
private SQLiteConnection waitForConnection(String sql, int connectionFlags,
CancellationSignal cancellationSignal) {
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;
}
此处tryAcquireNonPrimaryConnectionLocked
与 tryAcquirePrimaryConnectionLocked
最终都会调用openConnectionLocked
:
private SQLiteConnection tryAcquireNonPrimaryConnectionLocked(
String sql, int connectionFlags) {
// Try to acquire the next connection in the queue.
SQLiteConnection connection;
final int availableCount = mAvailableNonPrimaryConnections.size();
if (availableCount > 1 && sql != null) {
// 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) {
// 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();
if (mAvailablePrimaryConnection != null) {
openConnections += 1;
}
if (openConnections >= mMaxConnectionPoolSize) {
return null;
}
connection = openConnectionLocked(mConfiguration,
false /*primaryConnection*/); // might throw
finishAcquireConnectionLocked(connection, connectionFlags); // might throw
return connection;
}
进入openConnectionLocked:
private SQLiteConnection openConnectionLocked(SQLiteDatabaseConfiguration configuration,
boolean primaryConnection) {
final int connectionId = mNextConnectionId++;
return SQLiteConnection.open(this, configuration,
connectionId, primaryConnection); // might throw
}
直接调用SQLiteConnection
的静态成员函数open
:
static SQLiteConnection open(SQLiteConnectionPool pool,
SQLiteDatabaseConfiguration configuration,
int connectionId, boolean primaryConnection) {
SQLiteConnection connection = new SQLiteConnection(pool, configuration,
connectionId, primaryConnection);
try {
connection.open();
return connection;
} catch (SQLiteException ex) {
connection.dispose(false);
throw ex;
}
}
5行,调用native层的nativeOpen
函数,SQLiteConnection
对象的navite层类为android_database_SQLiteConnection
:
private void open() {
final String file = mConfiguration.path;
final int cookie = mRecentOperations.beginOperation("open", null, null);
try {
mConnectionPtr = nativeOpen(file, mConfiguration.openFlags,
mConfiguration.label,
NoPreloadHolder.DEBUG_SQL_STATEMENTS, NoPreloadHolder.DEBUG_SQL_TIME,
mConfiguration.lookasideSlotSize, mConfiguration.lookasideSlotCount);
} catch (SQLiteCantOpenDatabaseException e) {
final StringBuilder message = new StringBuilder("Cannot open database '")
.append(file).append('\'');
try {
// Try to diagnose for common reasons. If something fails in here, that's fine;
// just swallow the exception.
final Path path = FileSystems.getDefault().getPath(file);
final Path dir = path.getParent();
if (!Files.isDirectory(dir)) {
message.append(": Directory ").append(dir).append(" doesn't exist");
} else if (!Files.exists(path)) {
message.append(": File ").append(path).append(" doesn't exist");
} else if (!Files.isReadable(path)) {
message.append(": File ").append(path).append(" is not readable");
} else if (Files.isDirectory(path)) {
message.append(": Path ").append(path).append(" is a directory");
} else {
message.append(": Unknown reason");
}
} catch (Throwable th) {
message.append(": Unknown reason; cannot examine filesystem: ")
.append(th.getMessage());
}
throw new SQLiteCantOpenDatabaseException(message.toString(), e);
} finally {
mRecentOperations.endOperation(cookie);
}
setPageSize();
setForeignKeyModeFromConfiguration();
setWalModeFromConfiguration();
setJournalSizeLimit();
setAutoCheckpointInterval();
setLocaleFromConfiguration();
setCustomFunctionsFromConfiguration();
executePerConnectionSqlFromConfiguration(0);
}
static jlong nativeOpen(JNIEnv* env, jclass clazz, jstring pathStr, jint openFlags,
jstring labelStr, jboolean enableTrace, jboolean enableProfile, jint lookasideSz,
jint lookasideCnt) {
int sqliteFlags;
if (openFlags & SQLiteConnection::CREATE_IF_NECESSARY) {
sqliteFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
} else if (openFlags & SQLiteConnection::OPEN_READONLY) {
sqliteFlags = SQLITE_OPEN_READONLY;
} else {
sqliteFlags = SQLITE_OPEN_READWRITE;
}
const char* pathChars = env->GetStringUTFChars(pathStr, NULL);
String8 path(pathChars);
env->ReleaseStringUTFChars(pathStr, pathChars);
const char* labelChars = env->GetStringUTFChars(labelStr, NULL);
String8 label(labelChars);
env->ReleaseStringUTFChars(labelStr, labelChars);
sqlite3* db;
int err = sqlite3_open_v2(path.string(), &db, sqliteFlags, NULL);
if (err != SQLITE_OK) {
throw_sqlite3_exception_errcode(env, err, "Could not open database");
return 0;
}
if (lookasideSz >= 0 && lookasideCnt >= 0) {
int err = sqlite3_db_config(db, SQLITE_DBCONFIG_LOOKASIDE, NULL, lookasideSz, lookasideCnt);
if (err != SQLITE_OK) {
ALOGE("sqlite3_db_config(..., %d, %d) failed: %d", lookasideSz, lookasideCnt, err);
throw_sqlite3_exception(env, db, "Cannot set lookaside");
sqlite3_close(db);
return 0;
}
}
// Check that the database is really read/write when that is what we asked for.
if ((sqliteFlags & SQLITE_OPEN_READWRITE) && sqlite3_db_readonly(db, NULL)) {
throw_sqlite3_exception(env, db, "Could not open the database in read/write mode.");
sqlite3_close(db);
return 0;
}
// Set the default busy handler to retry automatically before returning SQLITE_BUSY.
err = sqlite3_busy_timeout(db, BUSY_TIMEOUT_MS);
if (err != SQLITE_OK) {
throw_sqlite3_exception(env, db, "Could not set busy timeout");
sqlite3_close(db);
return 0;
}
// Register custom Android functions.
err = register_android_functions(db, UTF16_STORAGE);
if (err) {
throw_sqlite3_exception(env, db, "Could not register Android SQL functions.");
sqlite3_close(db);
return 0;
}
// Create wrapper object.
SQLiteConnection* connection = new SQLiteConnection(db, openFlags, path, label);
// Enable tracing and profiling if requested.
if (enableTrace) {
sqlite3_trace(db, &sqliteTraceCallback, connection);
}
if (enableProfile) {
sqlite3_profile(db, &sqliteProfileCallback, connection);
}
ALOGV("Opened connection %p with label '%s'", db, label.string());
return reinterpret_cast<jlong>(connection);
21行,声明一个sqlite3连接指针,22行,直接调用sqlite3 API 函数sqlite3_open_v2,打开了一个数据库并建立了连接,接着29、46、54行分别调用sqlite3 API 函数进行一些数据库配置。到此,数据库已经成功打开(不出意外的话)。
返回到SQLiteSession.executeForCursorWindow中:
try {
return mConnection.executeForCursorWindow(sql, bindArgs,
window, startPos, requiredPos, countAllRows,
cancellationSignal); // might throw
} finally {
releaseConnection(); // might throw
调用SQLiteConnection的executeForCursorWindow函数:
public int executeForCursorWindow(String sql, Object[] bindArgs,
CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
CancellationSignal cancellationSignal) {
if (sql == null) {
throw new IllegalArgumentException("sql must not be null.");
}
if (window == null) {
throw new IllegalArgumentException("window must not be null.");
}
window.acquireReference();
try {
int actualPos = -1;
int countedRows = -1;
int filledRows = -1;
final int cookie = mRecentOperations.beginOperation("executeForCursorWindow",
sql, bindArgs);
try {
final PreparedStatement statement = acquirePreparedStatement(sql);
try {
throwIfStatementForbidden(statement);
bindArguments(statement, bindArgs);
applyBlockGuardPolicy(statement);
attachCancellationSignal(cancellationSignal);
try {
final long result = nativeExecuteForCursorWindow(
mConnectionPtr, statement.mStatementPtr, window.mWindowPtr,
startPos, requiredPos, countAllRows);
actualPos = (int)(result >> 32);
countedRows = (int)result;
filledRows = window.getNumRows();
window.setStartPosition(actualPos);
return countedRows;
} finally {
detachCancellationSignal(cancellationSignal);
}
} finally {
releasePreparedStatement(statement);
}
} catch (RuntimeException ex) {
mRecentOperations.failOperation(cookie, ex);
throw ex;
} finally {
if (mRecentOperations.endOperationDeferLog(cookie)) {
mRecentOperations.logOperation(cookie, "window='" + window
+ "', startPos=" + startPos
+ ", actualPos=" + actualPos
+ ", filledRows=" + filledRows
+ ", countedRows=" + countedRows);
}
}
} finally {
window.releaseReference();
}
}
关键点在942行,调用native层nativeExecuteForCursorWindow
:到android_database_SQLiteConnection
中找这个函数:
这儿直接贴出源码,在源码的三个关键位置已经做了注释
static jlong nativeExecuteForCursorWindow(JNIEnv* env, jclass clazz,
jlong connectionPtr, jlong statementPtr, jlong windowPtr,
jint startPos, jint requiredPos, jboolean countAllRows) {
SQLiteConnection* connection = reinterpret_cast<SQLiteConnection*>(connectionPtr);
sqlite3_stmt* statement = reinterpret_cast<sqlite3_stmt*>(statementPtr);
CursorWindow* window = reinterpret_cast<CursorWindow*>(windowPtr);
status_t status = window->clear();
if (status) {
String8 msg;
msg.appendFormat("Failed to clear the cursor window, status=%d", status);
throw_sqlite3_exception(env, connection->db, msg.string());
return 0;
}
int numColumns = sqlite3_column_count(statement);
status = window->setNumColumns(numColumns);
if (status) {
String8 msg;
msg.appendFormat("Failed to set the cursor window column count to %d, status=%d",
numColumns, status);
throw_sqlite3_exception(env, connection->db, msg.string());
return 0;
}
int retryCount = 0;
int totalRows = 0;
int addedRows = 0;
bool windowFull = false;
bool gotException = false;
while (!gotException && (!windowFull || countAllRows)) {
//调用sqlite3 库函数sqlite3_step,单步执行sqlite程序,当一行数据获取结束,返回SQLITE_ROW,获取返回错误码
int err = sqlite3_step(statement);
//SQLITE_ROW已经表示获取到一行数据了,可以调用相应的库函数拿去数据了
if (err == SQLITE_ROW) {
LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows);
retryCount = 0;
totalRows += 1;
// Skip the row if the window is full or we haven't reached the start position yet.
if (startPos >= totalRows || windowFull) {
continue;
}
//自定义函数,用于拿去数据并写入到共享内存块window中
CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
if (cpr == CPR_FULL && addedRows && startPos + addedRows <= requiredPos) {
// We filled the window before we got to the one row that we really wanted.
// Clear the window and start filling it again from here.
// TODO: Would be nicer if we could progressively replace earlier rows.
window->clear();
window->setNumColumns(numColumns);
startPos += addedRows;
addedRows = 0;
cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
}
if (cpr == CPR_OK) {
addedRows += 1;
} else if (cpr == CPR_FULL) {
windowFull = true;
} else {
gotException = true;
}
} else if (err == SQLITE_DONE) {
// All rows processed, bail
LOG_WINDOW("Processed all rows");
break;
} else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
// The table is locked, retry
LOG_WINDOW("Database locked, retrying");
if (retryCount > 50) {
ALOGE("Bailing on database busy retry");
throw_sqlite3_exception(env, connection->db, "retrycount exceeded");
gotException = true;
} else {
// Sleep to give the thread holding the lock a chance to finish
usleep(1000);
retryCount++;
}
} else {
throw_sqlite3_exception(env, connection->db);
gotException = true;
}
}
LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows"
"to the window in %d bytes",
statement, totalRows, addedRows, window->size() - window->freeSpace());
sqlite3_reset(statement);
// Report the total number of rows on request.
if (startPos > totalRows) {
ALOGE("startPos %d > actual rows %d", startPos, totalRows);
}
if (totalRows > 0 && addedRows == 0) {
String8 msg;
msg.appendFormat("Row too big to fit into CursorWindow requiredPos=%d, totalRows=%d",
requiredPos, totalRows);
throw_sqlite3_exception(env, SQLITE_TOOBIG, NULL, msg.string());
return 0;
}
jlong result = jlong(startPos) << 32 | jlong(totalRows);
return result;
}
接着看看copyRow
的实现:
static CopyRowResult copyRow(JNIEnv* env, CursorWindow* window,
sqlite3_stmt* statement, int numColumns, int startPos, int addedRows) {
// Allocate a new field directory for the row.
status_t status = window->allocRow();
if (status) {
LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d, error=%d",
startPos, addedRows, status);
return CPR_FULL;
}
// Pack the row into the window.
CopyRowResult result = CPR_OK;
for (int i = 0; i < numColumns; i++) {
int type = sqlite3_column_type(statement, i);
if (type == SQLITE_TEXT) {
// TEXT data
const char* text = reinterpret_cast<const char*>(
sqlite3_column_text(statement, i));
// SQLite does not include the NULL terminator in size, but does
// ensure all strings are NULL terminated, so increase size by
// one to make sure we store the terminator.
size_t sizeIncludingNull = sqlite3_column_bytes(statement, i) + 1;
status = window->putString(addedRows, i, text, sizeIncludingNull);
if (status) {
LOG_WINDOW("Failed allocating %zu bytes for text at %d,%d, error=%d",
sizeIncludingNull, startPos + addedRows, i, status);
result = CPR_FULL;
break;
}
LOG_WINDOW("%d,%d is TEXT with %zu bytes",
startPos + addedRows, i, sizeIncludingNull);
} else if (type == SQLITE_INTEGER) {
// INTEGER data
int64_t value = sqlite3_column_int64(statement, i);
status = window->putLong(addedRows, i, value);
if (status) {
LOG_WINDOW("Failed allocating space for a long in column %d, error=%d",
i, status);
result = CPR_FULL;
break;
}
LOG_WINDOW("%d,%d is INTEGER %" PRId64, startPos + addedRows, i, value);
} else if (type == SQLITE_FLOAT) {
// FLOAT data
double value = sqlite3_column_double(statement, i);
status = window->putDouble(addedRows, i, value);
if (status) {
LOG_WINDOW("Failed allocating space for a double in column %d, error=%d",
i, status);
result = CPR_FULL;
break;
}
LOG_WINDOW("%d,%d is FLOAT %lf", startPos + addedRows, i, value);
} else if (type == SQLITE_BLOB) {
// BLOB data
const void* blob = sqlite3_column_blob(statement, i);
size_t size = sqlite3_column_bytes(statement, i);
status = window->putBlob(addedRows, i, blob, size);
if (status) {
LOG_WINDOW("Failed allocating %zu bytes for blob at %d,%d, error=%d",
size, startPos + addedRows, i, status);
result = CPR_FULL;
break;
}
LOG_WINDOW("%d,%d is Blob with %zu bytes",
startPos + addedRows, i, size);
} else if (type == SQLITE_NULL) {
// NULL field
status = window->putNull(addedRows, i);
if (status) {
LOG_WINDOW("Failed allocating space for a null in column %d, error=%d",
i, status);
result = CPR_FULL;
break;
}
LOG_WINDOW("%d,%d is NULL", startPos + addedRows, i);
} else {
// Unknown data
ALOGE("Unknown column type when filling database window");
throw_sqlite3_exception(env, "Unknown column type when filling window");
result = CPR_ERROR;
break;
}
}
// Free the last row if if was not successfully copied.
if (result != CPR_OK) {
window->freeLastRow();
}
return result;
}
逻辑是,对于一行中的每一列,先利用sqlite3_column_type
库函数获取该列的数据类型,对于不同的数据类型,调用sqlite3_column_(type)
获取数据,在将该输入写入到共享内存window中。
到此,所有流程都已经分析结束了,其中有很多细节都没有分析,比如最后这儿,向window中写入数据时是怎么实现的等等,这些读者可以自行查看源码了解。