当前位置: 代码迷 >> Android >> Android Content Provider在应用程序其间共享数据的原理分析
  详细解决方案

Android Content Provider在应用程序其间共享数据的原理分析

热度:101   发布时间:2016-04-28 04:49:58.0
Android Content Provider在应用程序之间共享数据的原理分析

       本文参考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

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;	}	......}
       我们这里只关注window.writeToParcel(data, 0)。详细解释请看对应的博客或者图书。

       如图:第二步,省略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就获取了数据。

  相关解决方案