本文參考Android应用程序组件Content Provider在应用程序之间共享数据的原理分析http://blog.csdn.net/luoshengyang/article/details/6967204和《Android系统源码情景分析》,作者罗升阳。
0、总图流程图例如以下:
整体类图:
1、MainActivity进程向AriticlesProvider进程发送IContentProvider.QUERY_TRANSACTION
如图:第一步
~/Android/frameworks/base/core/java/android/content
----ContentProviderNative.java
final class ContentProviderProxy implements IContentProvider {
......
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sortOrder) throws RemoteException {
//TODO make a pool of windows so we can reuse memory dealers
CursorWindow window = new CursorWindow(false /* window will be used remotely */);
BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
IBulkCursor bulkCursor = bulkQueryInternal(
url, projection, selection, selectionArgs, sortOrder,
adaptor.getObserver(), window,
adaptor);
if (bulkCursor == null) {
return null;
}
return adaptor;
}
......
(1)创建了CursorWindow对象。
(2)创建类BulkCursorToCursorAdaptor对象。
(3)调用bulkQueryInternal。
~/Android/frameworks/base/core/java/android/content
----ContentProviderNative.java
我们这里仅仅关注window.writeToParcel(data, 0)。详解请看相应的博客或者图书。final class ContentProviderProxy implements IContentProvider { ...... private IBulkCursor bulkQueryInternal( Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder, IContentObserver observer, CursorWindow window, BulkCursorToCursorAdaptor adaptor) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IContentProvider.descriptor); 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.writeString(selection); if (selectionArgs != null) { length = selectionArgs.length; } else { length = 0; } data.writeInt(length); for (int i = 0; i < length; i++) { data.writeString(selectionArgs[i]); } data.writeString(sortOrder); data.writeStrongBinder(observer.asBinder()); window.writeToParcel(data, 0); // Flag for whether or not we want the number of rows in the // cursor and the position of the "_id" column index (or -1 if // non-existent). Only to be returned if binder != null. final boolean wantsCursorMetadata = (adaptor != null); data.writeInt(wantsCursorMetadata ?
1 : 0); mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); DatabaseUtils.readExceptionFromParcel(reply); IBulkCursor bulkCursor = null; IBinder bulkCursorBinder = reply.readStrongBinder(); if (bulkCursorBinder != null) { bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder); if (wantsCursorMetadata) { int rowCount = reply.readInt(); int idColumnPosition = reply.readInt(); if (bulkCursor != null) { adaptor.set(bulkCursor, rowCount, idColumnPosition); } } } data.recycle(); reply.recycle(); return bulkCursor; } ...... }
如图:第二步。省略binder_transaction传输过程,由于上面已经分析过了。
如图:第三步
~/Android/frameworks/base/core/java/android/content
----ContentProviderNative.java
abstract public class ContentProviderNative extends Binder implements IContentProvider {
......
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
try {
switch (code) {
case QUERY_TRANSACTION:
{
data.enforceInterface(IContentProvider.descriptor);
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();
}
}
// String selection, String[] selectionArgs...
String selection = data.readString();
num = data.readInt();
String[] selectionArgs = null;
if (num > 0) {
selectionArgs = new String[num];
for (int i = 0; i < num; i++) {
selectionArgs[i] = data.readString();
}
}
String sortOrder = data.readString();
IContentObserver observer = IContentObserver.Stub.
asInterface(data.readStrongBinder());
CursorWindow window = CursorWindow.CREATOR.createFromParcel(data);
// Flag for whether caller wants the number of
// rows in the cursor and the position of the
// "_id" column index (or -1 if non-existent)
// Only to be returned if binder != null.
boolean wantsCursorMetadata = data.readInt() != 0;
IBulkCursor bulkCursor = bulkQuery(url, projection, selection,
selectionArgs, sortOrder, observer, window);
reply.writeNoException();
if (bulkCursor != null) {
reply.writeStrongBinder(bulkCursor.asBinder());
if (wantsCursorMetadata) {
reply.writeInt(bulkCursor.count());
reply.writeInt(BulkCursorToCursorAdaptor.findRowIdColumnIndex(
bulkCursor.getColumnNames()));
}
} else {
reply.writeStrongBinder(null);
}
return true;
}
......
}
} catch (Exception e) {
DatabaseUtils.writeExceptionToParcel(reply, e);
return true;
}
return super.onTransact(code, data, reply, flags);
}
......
}
当中,CursorWindow window = CursorWindow.CREATOR.createFromParcel(data);详解请看博客或者书。
如图:第四步
~/Android/frameworks/base/core/java/android/content
----ContentProvider.java
public abstract class ContentProvider implements ComponentCallbacks {
......
class Transport extends ContentProviderNative {
......
public IBulkCursor bulkQuery(Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
IContentObserver observer, CursorWindow window) {
......
Cursor cursor = ContentProvider.this.query(uri, projection,
selection, selectionArgs, sortOrder);
......
return new CursorToBulkCursorAdaptor(cursor, observer,
ContentProvider.this.getClass().getName(),
hasWritePermission(uri), window);
}
......
}
......
}
主要做了以下几件事:
(1)调用AriticlesProvider的query方法。获取了SQLiteCursor对象。
(2)由cursor和window对象,形成CursorToBulkCursorAdaptor对象。
如图。第五步
if (bulkCursor != null) {
reply.writeStrongBinder(bulkCursor.asBinder());
if (wantsCursorMetadata) {
reply.writeInt(bulkCursor.count());
reply.writeInt(BulkCursorToCursorAdaptor.findRowIdColumnIndex(
bulkCursor.getColumnNames()));
}
}
传递CursorToBulkCursorAdaptor对象,例如以下图:
如图:第六步。省略binder_transaction传输过程,由于上面已经分析过了。
如图:第七步
~/Android/frameworks/base/core/java/android/content
----ContentProviderNative.java
IBulkCursor bulkCursor = null;
IBinder bulkCursorBinder = reply.readStrongBinder();
if (bulkCursorBinder != null) {
bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder);
if (wantsCursorMetadata) {
int rowCount = reply.readInt();
int idColumnPosition = reply.readInt();
if (bulkCursor != null) {
adaptor.set(bulkCursor, rowCount, idColumnPosition);
}
}
}
bulkCursor为BulkCursorProxy对象例如以下:
adaptor.set(bulkCursor, rowCount, idColumnPosition);
把BulkCursorProxy对象放入到BulkCursorToCursorAdaptor对象的句柄变量mBulkCursor中。
如图:第八步
return new CursorWrapperInner(qCursor, provider);
最后返回了这个对象,qCursor是
BulkCursorToCursorAdaptor对象,provider为ContentProviderProxy对象。
至此,我们形成了下图:
进程间通信结束了,以下我们分析怎样应用匿名共享内存来数据传输。
在前面的一篇文章Android Content Provider的启动过程源码分析http://blog.csdn.net/jltxgcy/article/details/37725749。最后获取了ContentProviderProxy对象,通过进程间通信来传递数据。
public class ArticlesAdapter {
......
private ContentResolver resolver = null;
public ArticlesAdapter(Context context) {
resolver = context.getContentResolver();
}
......
public Article getArticleByPos(int pos) {
Uri uri = ContentUris.withAppendedId(Articles.CONTENT_POS_URI, pos);
String[] projection = new String[] {
Articles.ID,
Articles.TITLE,
Articles.ABSTRACT,
Articles.URL
};
Cursor cursor = resolver.query(uri, projection, null, null, Articles.DEFAULT_SORT_ORDER);
if (!cursor.moveToFirst()) {
return null;
}
int id = cursor.getInt(0);
String title = cursor.getString(1);
String abs = cursor.getString(2);
String url = cursor.getString(3);
return new Article(id, title, abs, url);
}
}
我们不分析具体过程,首先BulkCursorToCursorAdaptor对象里面的成员变量mBulkCursor通过进程间通信的方式,找到CursorToBulkCursorAdaptor对象。通过里面的成员函数mCursor查询出数据,而且保存在mWindows所指向的匿名共享内存。
而BulkCursorToCursorAdaptor通过成员变量mWindow来訪问同样的匿名共享内存的。这样MainActivity就获取了数据。
假设是删除数据,可能不须要读写匿名共享内存,仅仅要通过mBulkCursor通过进程间通信的方式的操作和读取返回结果。