写在前面:本文是根据大神的博客http://blog.csdn.net/luoshengyang/article/details/6967204,对android5.0系统进行了分析
分析之前先说一下,自己对Content Provider进程间通信的基本理解。个人认为,Android中进程间通信的套路都是基本一致。基本和AIDL差不多。其实AIDL本身就是用来在进程之间通信的。这里先说一下进程间通信的大体套路。假设client进程要向server进程传递数据或访问数据,首先,两个进程要拥有相同的接口(假设为A),因为我们我们要使他们的对象相同,做到面向对象编程,即调用client进程的A对象,就像调用server进程的A对象一样。第二,进程间的通信是用代理模式来实现的,所以在server进程要有一个真正用于提供服务的类AA(实现了A),在client进程会有一个AProxy类,用来代表AA,这样我们在client做的操作就可以有Aproxy传到server的AA中了。第三,既然是进程间通信,那么Binder也是必不可少的。它会将在client端所做的操作传递到server端。这里我们附一张图来说明一下:现在我们以content provider,来看一下进程间通信的实现机制:。
好了,回到正题,说一下content provider的通信。它的通信和进程间的通信是基本一致的,但是,它也有些不同,在Content Provider的通信中会有一个CursorWindow类,它的作用是用来储存查询到的数据,client端和server端会共享这个CursorWindow类型的对象,从而实现数据的交流。
在上一节我们说过content provider的启动。content provider启动的最后阶段,会调用AMS的getContentProvider()方法来返回一个ContentProviderHolder型的holder对象,它里面包含了已经加载好的provider对象,但是这里的provider对象并不是真正的服务端的provider对象,而是一个ContentProviderProxy对象,它里面包含了用于进程间通信的binder。下面让我们来看一下这个ContentProviderProxy对象是如何获得的:
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
从上段代码可以看出,holder是通过一个AMS来获得的,而这个AMS对象其实也不是真正的AMS,它只是AMS位于client端的代理对象(因为AMS在系统进程中运行),它的类型为ActivityManagerProxy。知道AMS后,我们在看一下它是如何获得holder的
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;
}
case GET_CONTENT_PROVIDER_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
IApplicationThread app = ApplicationThreadNative.asInterface(b);
......
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;
}
这一段是在AMS的onTransact(),我们只截取了一部分。在这会调用我们在上一节说过的getContentProvider(),它会返回一个已经加载好的ContentProviderHolder对象,然后将holder对象写入reply,那么,我们查看一下它是如何把provider写入reply的:
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);
}
这一步是在holder中进行的,它会把含有的provider,提供给Parcel对象。写入完毕之后,把reply返回给client进程。这样client进程就可以在reply中获得已经加载好的provider。这样和AMS进程间的通信就已经完成了。下一步就是把它转化成client端的provider,即ContentProviderProxy对象。
public static final Parcelable.Creator CREATOR
= new Parcelable.Creator() {
@Override
public ContentProviderHolder createFromParcel(Parcel source) {
return new ContentProviderHolder(source);
}
@Override
public ContentProviderHolder[] newArray(int size) {
return new ContentProviderHolder[size];
}
};
private ContentProviderHolder(Parcel source) {
info = ProviderInfo.CREATOR.createFromParcel(source);
provider = ContentProviderNative.asInterface(
source.readStrongBinder());
connection = source.readStrongBinder();
noReleaseNeeded = source.readInt() != 0;
}
从上面两段代码可知,contentProviderHolder是根据reply来创建的,而provider是通过ContentProviderNative的asInterface函数来创建的。asInterface的参数是AMS传过来的provider,同时这个provider(Transport类型)也是一个binder对象。这样就把这个binder放到了client端的provider中,以后就可用它来和server端进行通信了。这里简单说一下asInterface(),它会生成一个ContentProviderProxy对象,并把binder存到此对象的mRemote上,以后用来和server端的provider进行通信了。
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 {
data.writeInterfaceToken(IContentProvider.descriptor);
//省略查询参数的封装
......
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);
adaptor.initialize(d);
} else {
adaptor.close();
adaptor = null;
}
return adaptor;
}
......
}
在client端得到provider后,会调用query(),进行查询,这其实是发了一个查询请求给server端的provider。然后server端的provider会返回一个BulkCursorDescriptor对象,用来生成client的BulkCursorToCursorAdaptor对象adaptor。此后的增删改查等操作,都会通过这个adaptor来完成。下面我们来看一下它的通信过程。case QUERY_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
//参数获得
......
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 {
// Close cursor if an exception was thrown while constructing the adaptor.
if (adaptor != null) {
adaptor.close();
}
if (cursor != null) {
cursor.close();
}
}
} else {
reply.writeNoException();
reply.writeInt(0);
}
return true;
}
这里的onTransact的执行是在你的主线程执行的,因为你自己实现的ContentProvider继承了ContentProviderNative。正是因为继承了ContentProviderNative(实现了IContentProvider接口),我们才能在两个进程间通信。这也就是AIDL的套路。好的,回到正题,在onTransact中会调用你自己实现的query方法。在我们自己实现的provider中会调用SQLiteDataBase的query(),最终会调用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();
}
}
在进行查询之前,会根据传递进来的各种参数生成一个查询语句,接下来我们看一些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();
}
}
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;
}
这里会使用传进来的参数生成一个SQLiteCursor类型的对象cursor,此对象的父类有Cursor,最终也会将此cursor传递给client进程。这里我们需要住一个是这个SQLiteQuery类型的query对象,最终的查询操作是由他执行的。这时,你可能会好奇,为什么没有执行查询操作。这是因为对数据库的操作代价较高,它奉行的是懒加载策略,即到需要查询时才查询。
得到cursor后,就可以用此cursor来生成一个server端的CursorToBulkCursorAdaptor类型的adaptor对象了,然后用此adaptor,生成一个BulkCursorDescriptor 类型的对象。
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;
}
}
在这段代码,数据库才真正执行查询操作,并将其放入CursorWindow中。因为要返回一个BulkCursorDescriptor类型的对象,所以它会先新建一个BulkCursorDescriptor类型的对象d,然后对其进行填充。比较重要的一步是mCursor.getCount();我们来看一下它是如何执行的,这里的mCursor的类型是SQLiteCursor。
public int getCount() {
if (mCount == NO_COUNT) {
fillWindow(0);
}
return mCount;
}
它会又去转调fillWindow()
private void fillWindow(int requiredPos) {
clearOrCreateWindow(getDatabase().getPath());
try {
if (mCount == NO_COUNT) {
int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos, 0);
mCount = mQuery.fillWindow(mWindow, startPos, requiredPos, true);
mCursorWindowCapacity = mWindow.getNumRows();
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "received count(*) from native_fill_window: " + mCount);
}
} else {
int startPos = DatabaseUtils.cursorPickFillWindowStartPosition(requiredPos,
mCursorWindowCapacity);
mQuery.fillWindow(mWindow, startPos, requiredPos, false);
}
}
......
}
在这个代码块中,它会调用父类(AbstractWindowedCursor)的clearOrCreateWindow(),去创建一个mWindow,用来保存数据
protected void clearOrCreateWindow(String name) {
if (mWindow == null) {
mWindow = new CursorWindow(name);
} else {
mWindow.clear();
}
}
关于CursorWindow是如何创建的,我们这里就不深究,它涉及到了framework层的代码。既然mWindow已经创建好了,那我们就来看一下它是如何查询并保存的数据的。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;
}
.......
}
前面,我们说过,cursor引用的query使用来查询数据的,那我们现在就来看一下。先说一下的它的两个形参,startPos:用来表示查询到的数据应该从mWindow什么位置开始保存public int executeForCursorWindow(String sql, Object[] bindArgs,
CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
int connectionFlags, CancellationSignal cancellationSignal) {
......
acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
try {
return mConnection.executeForCursorWindow(sql, bindArgs,
window, startPos, requiredPos, countAllRows,
cancellationSignal); // might throw
} finally {
releaseConnection(); // might throw
}
}
从上面的代码块中可以看出,他回去转调SQLiteConnection的executeForCursorWindow(),这个方法将查询数据库得到的信息保存在mWindow中,至于具体是怎样获得的,我们就不去具体分析,这涉及到framework层。SQLiteConnection包装了sqlite3对象,可以真正的去访问数据库。
最后,我们终于得到了填满数据的mWindow,这样就可将其封装到BulkCursorDescriptor类型的对象d中,然后将其放到要发送的Parcel中。
public void writeToParcel(Parcel out, int flags) {
out.writeStrongBinder(cursor.asBinder());
out.writeStringArray(columnNames);
out.writeInt(wantsAllOnMoveCalls ? 1 : 0);
out.writeInt(count);
if (window != null) {
out.writeInt(1);
window.writeToParcel(out, flags);
} else {
out.writeInt(0);
}
}
从上面的代码可以看到,我们将SQLiteCursor类型的变量cursor转化成了BInder,放入out中,然后又将window和window中的数据放入out,这样我们就可以发送给client端了。
BulkCursorDescriptor d = BulkCursorDescriptor.CREATOR.createFromParcel(reply);
adaptor.initialize(d);
client端会将发过来的数据整合成一个BulkCursorDescriptor类型的对象,然后用其实例化一个BulkCursorToCursorAdaptor类型的对象adaptor,并将其作为Cursor类型的对象返回,这样,我们在client端就可以进行数据的增删该查了