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就获取了数据。

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

时间: 2024-12-27 18:07:26

Android Content Provider在应用程序之间共享数据的原理分析的相关文章

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

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6967204 在Android系统中,不同的应用程序是不能直接读写对方的数据文件的,如果它们想共享数据的话,只能通过 Content Provider组件来实现.那么,Content Provider组件又是如何突破应用程序边界权限控制来实现在不同的应用程序之间共享数据的呢?在前面的文章中,我们已经简要介绍过它是通过 Binder进程间通信机制以

Android Content Provider Security(转)

四大组件之一-content provider安全详解 原帖地址:http://drops.wooyun.org/tips/4314 0x00 科普 内容提供器用来存放和获取数据并使这些数据可以被所有的应用程序访问.它们是应用程序之间共享数据的唯一方法:不包括所有Android软件包都能访问的公共储存区域.Android为常见数据类型(音频,视频,图像,个人联系人信息,等等)装载了很多内容提供器.你可以看到在android.provider包里列举了一些.你还能查询这些提供器包含了什么数据.当然

iOS应用程序间共享数据(转)

我们知道iOS由于沙盒的存在,应用程序不能越过自己的区域去访问别的存储空间的内容,不过可能有许多场景我们需要在应用程序之间共享数据,比如多个应用共用用户名密码进行登录等.虽然我们不能直接通过文件系统来分享数据,不过还是有些方法可以实现,为了方便说明,这里同时创建了两个工程Example1和Example2,实现这两个app之间的信息共享,Example1负责写数据,Example2负责读数据,具体的demo代码可以到这里获取 UIPasteboard 剪贴板是应用程序之间传递数据的简单方式,建议

不同App之间共享数据

我们知道iOS由于沙盒的存在,应用程序不能越过自己的区域去访问别的存储空间的内容,不过可能有许多场景我们需要在应用程序之间共享数据,比如多个应用共用用户名密码进行登录等.虽然我们不能直接通过文件系统来共享数据,不过还是有些方法可以实现. 这里,我们新建两个工程,T1:负责写数据,T2:负责读数据. 方法一:UIPasteboard 剪贴板是应用程序之间传递数据的简单方式,建议不要使用全局的粘贴板,而是自己根据名字创建一个新的粘贴板,防止其它地方全局拷贝的影响.然后把需要共享的内容复制到粘贴板,粘

iOS应用程序间共享数据

我们知道iOS由于沙盒的存在,应用程序不能越过自己的区域去访问别的存储空间的内容,不过可能有许多场景我们需要在应用程序之间共享数据,比如多个应用共用用户名密码进行登录等.虽然我们不能直接通过文件系统来分享数据,不过还是有些方法可以实现,为了方便说明,这里同时创建了两个工程send和receive,实现这两个app之间的信息共享,send负责写数据,receive负责读数据,具体的demo代码可以到这里获取 UIPasteboard 剪贴板是应用程序之间传递数据的简单方式,建议不要使用全局的粘贴板

Android Content Provider的启动过程源代码分析

本文参考Android应用程序组件Content Provider的启动过程源代码分析http://blog.csdn.net/luoshengyang/article/details/6963418和<Android系统源代码情景分析>,作者罗升阳. 0.总图流程图如下: 1.MainActivity进程向ActivityServiceManager主线程发送GET_CONTENT_PORVIDER_TRANSACTION 如下图: 如图:第一步 ~/Android/frameworks/b

Android Content Provider Guides

Android Content Provider Guides Content Providers管理对结构化数据集的访问.它们包装数据,并且提供一种定义数据安全的机制. Content providers是不同进程间数据连接的标准接口. 要获取content provider中的数据,需要运用你的应用中的 Context中的ContentResolver对象作为一个client来和provider交互. 这个provider对象是一个实现了ContentProvider接口的类的对象.Prov

Android多线程研究(5)——线程之间共享数据

一.如果是每个线程都执行相同的代码,则可以使用同一个Runnable来实现共享 public class MultiThreadShareData { public static void main(String[] args) { new Thread(new ShareData()).start(); new Thread(new ShareData()).start(); } static class ShareData implements Runnable{ private int j

android content provider 中的URL解析总是出问题?求指导!!!

java.lang.IllegalArgumentException: Unknown URL content:// 不管是自己写或者用别的的代码在我的eclipse中都是报这个错误 很怪,我的URL地址绝对没有写错,是不是和使用版本有关系?我的google提供的eclipse,sdk用的2.3.3 android content provider 中的URL解析总是出问题?求指导!!!