年终(农历年)的一篇任务文档。
本篇剖析ContentProvider的query操作,包括了以下内容:
通常,我们会使用下面的方式使用ContentProvider获取数据:
mContext.getContentResolver().query(...);
首先要拿到context对象,即上面代码中的“mContext”,这个context对象可能是Activity,Service或者Application。
其次调用“getContentResolver()”方法,但是无论是Activity,Service还是Application都没有重写该方法,所以使用的依然是它们共同的父类ContextWrapper中的“getContentResolver()”方法,代码实现如下:
@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}
上面代码中的“mBase”是ContextImpl对象,该对象是在Activity,Service和Application创建时被创建的。
ContextImpl.java中对getContentResolver的实现很简单:
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
“mContentResolver”是ApplicationContentResolver对象; ApplicationContentResolver是ContextImpl中的静态内部类,继承了ContextResolver。
ContentResolver的query方法实现逻辑如下:
代码如下(为了减少篇幅,删了部分代码):
public final @Nullable Cursor query(final @NonNull Uri uri, @Nullable String[] projection,
@Nullable String selection, @Nullable String[] selectionArgs,
@Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
Preconditions.checkNotNull(uri, "uri");
// 1.获取Provider
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
...
try {
//2.调用Provider的query方法
qCursor = unstableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
} catch (DeadObjectException e) {
...
unstableProviderDied(unstableProvider);
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
return null;
}
qCursor = stableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
}
if (qCursor == null) {
return null;
}
...
//3.返回cursor结果
// Wrap the cursor object into CursorWrapperInner object.
CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
stableProvider != null ? stableProvider : acquireProvider(uri));
stableProvider = null;
qCursor = null;
return wrapper;
} catch (RemoteException e) {
...
} finally {
...
//清理释放资源
}
}
ApplicationContentResolver重写了ContentResolver的acquireUnstableProvider(…)方法:
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
代码中的“mMainThread”是ActivityThread类型的对象,acquireProvider的代码实现如下:
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
//查找Provider是否已经存在
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
...
IActivityManager.ContentProviderHolder holder = null;
try {
//通过ActivityManagerService获取Provider
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
}
if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + auth);
return null;
}
...
//保存Provider的信息,以后使用
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
acquireExistingProvider(…)方法会从“mProviderMap”中查找依然active的目标Provider,如果能成功获取那么就直接使用; 否则就要通过ActivityManagerService来获取Provider。
至于ActivityManagerService的getContentProvider(…)方法是如何获取Provider的, 这里就不贴代码了,只简述相关逻辑。
首先查找provider是否已经运行,如果已经在运行,判断provider是否可以运行在多进程,如果可以,只是返回一个不包含Provider对象的ContentProviderHolder对象,让调用进程自己创建Provider对象; 否则就返回包含Provider对象的ContentProviderHolder对象。
如果Provider没有在运行,那么就创建ContentProviderRecord对象,然后判断Provider是否可以在多线程运行,如果可以就返回一个不包含Provider对象的ContentProviderHolder对象,让调用进程自己创建Provider对象; 否则就返回包含Provider对象的ContentProviderHolder对象。
对于新获取的provider,installProvider(…)方法会根据ActivityManagerService返回的结果来决定是否创建Provider对象,另外还会将相关Provider关信息存储到成员变量mLocalProviders,mProviderRefCountMap,mLocalProvidersByName和mProviderMap中。
那么ContentProviderHolder中Provider对象是什么? (两个进程的情况)
ActivityManagerService返回的是一个ContentProviderHolder对象,我们可以查看Server端ActivityManagerNative.java中的代码:
case GET_CONTENT_PROVIDER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
IApplicationThread app = ApplicationThreadNative.asInterface(b);
String name = data.readString();
int userId = data.readInt();
boolean stable = data.readInt() != 0;
ContentProviderHolder cph = getContentProvider(app, name, userId, stable);
reply.writeNoException();
if (cph != null) {
reply.writeInt(1);
cph.writeToParcel(reply, 0);
} else {
reply.writeInt(0);
}
return true;
}
我们看到从ActivityManagerService拿到ContentProviderHolder后,会调用起writeToParcel(…)方法,代码如下:
@Override
public void writeToParcel(Parcel dest, int flags) {
info.writeToParcel(dest, 0);
if (provider != null) {
dest.writeStrongBinder(provider.asBinder());
} else {
dest.writeStrongBinder(null);
}
dest.writeStrongBinder(connection);
dest.writeInt(noReleaseNeeded ? 1 : 0);
}
上面代码中的provider是什么?可以查看ActivityThread的installProvider(…)方法,下面这行代码揭示了provider是什么?
provider = localProvider.getIContentProvider();
“localProvider”就是ContentProvider的实例,比如ContactsProvider2; 而getIContentProvider()方法在ContentProvider.java中的实现如下:
public IContentProvider getIContentProvider() {
return mTransport;
}
“mTransport”指向的是Transport对象; Transport是ContentProvider.java的内部类,继承了ContentProviderNative类(继承了Binder),这就是server端执行query操作的地方。现在我们知道ContentProviderHold往parcel中写入的其实是Transport对象。
我看再看Client端拿到Server端返回的ContentProviderHolder对象后是怎么处理的,相关代码在ActivityManagerProxy类中,实现如下:
public ContentProviderHolder getContentProvider(IApplicationThread caller,
String name, int userId, boolean stable) throws RemoteException {
...
mRemote.transact(GET_CONTENT_PROVIDER_TRANSACTION, data, reply, 0);
...
ContentProviderHolder cph = null;
if (res != 0) {
cph = ContentProviderHolder.CREATOR.createFromParcel(reply);
}
data.recycle();
reply.recycle();
return cph;
}
这里通过ContentProviderHolder的createFromParcel方法构建了一个新的ContenProviderHolder对象,代码实现如下:
public static final Parcelable.Creator CREATOR
= new Parcelable.Creator() {
@Override
public ContentProviderHolder createFromParcel(Parcel source) {
return new ContentProviderHolder(source);
}
...
};
private ContentProviderHolder(Parcel source) {
info = ProviderInfo.CREATOR.createFromParcel(source);
provider = ContentProviderNative.asInterface(
source.readStrongBinder());
connection = source.readStrongBinder();
noReleaseNeeded = source.readInt() != 0;
}
ContentProviderHolder的provider成员是一个IContentProvider类型的变量,创建ContentProviderHolder对象时,provider也会赋值:
provider = ContentProviderNative.asInterface(
source.readStrongBinder());
provider指向的是一个ContentProviderProxy类型的对象。
经过上面的分析,我们知道对于Client端和Server端运行在两个不同进程的情况,Client端拿到的是ContentProviderProxy对象; 不过对于Client端和Server端运行在同一个进程的情况,拿到的就是Transport对象了。这整个过程就是binder通信的流程
现在回到ContentResolver的query方法中,通过上面的分析我们知道下面代码中的unstableProvider指向的就是ContentProviderProxy对象。
qCursor = unstableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
我们看一下ContentProviderProxy的query方法:
public Cursor query(String callingPkg, Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal)
throws RemoteException {
BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
try {
...
mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);
DatabaseUtils.readExceptionFromParcel(reply);
if (reply.readInt() != 0) {
BulkCursorDescriptor d = BulkCursorDescriptor.CREATOR.createFromParcel(reply);
adaptor.initialize(d);
} else {
adaptor.close();
adaptor = null;
}
return adaptor;
} catch (RemoteException ex) {
...
} catch (RuntimeException ex) {
...
} finally {
...
}
}
在这个方法的第一行就new了一个BulkCursorToCursorAdaptor对象,并定义了一个变量adaptor,指向新对象; 然后我们找return语句,发现最后返回的就是adaptor。
ContentResolver拿到ContentProviderProxy返回的结果后,又进行了包装,代码如下:
try {
qCursor = unstableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
} catch (DeadObjectException e) {
...
}
if (qCursor == null) {
return null;
}
...
// Wrap the cursor object into CursorWrapperInner object.
CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
stableProvider != null ? stableProvider : acquireProvider(uri));
ContentResolver返回的是一个CursorWrapperInner对象,也就是我们在App侧操作的cursor,其实就是CursorWrapperInner对象。
下面的类图展示了BulkCursorToCursorAdaptor的继承关系:
ContentProvider的getIContentProvider方法返回的是ContentProvider的内部类Transport对象; Transport继承了ContentProviderNative类。Client端的请求首先会到ContentProviderNative的onTransact方法的QUERY_TRANSACTION case中,在这个case中Transport的query会被调用,Transport的query会调用ContentProvider对象的query方法执行查询操作,代码如下:
case QUERY_TRANSACTION:
{
...
Cursor cursor = query(callingPkg, url, projection, selection, selectionArgs,
sortOrder, 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 {
...
} else {
...
}
return true;
}
以ContactProvider2为例,query返回的是SQLiteCursor对象,下面的流程图展示了SQLiteCursor的创建过程:
下面的图展示了类之间的继承关系:
CursorWindow是什么? CursorWindow is a buffer containing multiple cursor rows.也就是说CursorWindow是一个缓存,里面存储的是从数据库中查出的一行行数据。CursorWindow初始创建的时候是可读可写的,供本进程使用,但是当要跨进程时,传给远端进程的就是一个只读的“view”;典型例子就是服务端创建一个CursorWindow,填充数据,然后供客户端读取。
上面貼的代码中有下面几行:
adaptor = new CursorToBulkCursorAdaptor(cursor, observer,
getProviderName());
cursor = null;
BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();
adaptor = null;
CursorToBulkCursorAdaptor的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对象时,Cursor的getCount方法会被调用,这是第一次调用,SQLiteCursor的mCount会等于”NO_COUNT”,这时会创建CursorWindow。CursorWindow会有native调用,对应的JNI文件是android_database_cursorwindow.cpp, lib文件是CursorWindow.cpp。CursorWindow.cpp会创建一块匿名共享内存,并做好mmap,共享内存块的大小可以在”framework/base/core/res/res/values/config.xml“的config_cursorWindowSize字段中进行配置,默认是2048KB; 这块共享内存就是用来存储从DB中查询出的内容。
下面的图展示了上述内容,Client 端的CursorWindow是通过 createFromParcel(Parcel source) 方法创建的,Server端的内容是放入Parcel中通过Binder传递到Client端的。