Content Provider之间互相通信的源码浅析
来源:互联网 发布:上海软件项目经理工资 编辑:程序博客网 时间:2024/06/06 10:51
写在前面:本文是根据大神的博客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通信分析
1、client端provider的获得
在上一节我们说过content provider的启动。content provider启动的最后阶段,会调用AMS的getContentProvider()方法来返回一个ContentProviderHolder型的holder对象,它里面包含了已经加载好的provider对象,但是这里的provider对象并不是真正的服务端的provider对象,而是一个ContentProviderProxy对象,它里面包含了用于进程间通信的binder。下面让我们来看一下这个ContentProviderProxy对象是如何获得的:
1.1获得holder:
holder = ActivityManagerNative.getDefault().getContentProvider( getApplicationThread(), auth, userId, stable);
从上段代码可以看出,holder是通过一个AMS来获得的,而这个AMS对象其实也不是真正的AMS,它只是AMS位于client端的代理对象(因为AMS在系统进程中运行),它的类型为ActivityManagerProxy。知道AMS后,我们在看一下它是如何获得holder的1.2ActivityManagerProxy的getContentProvider()
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; }
1.3这里的mBinder是AMS进程发过来的binder对象,用来和真正的AMS通信,从上面的代码可以看出,它会向真正的AMS请求provider对象,具体实现如下:
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对象。
1.4我们来看一下ContentProviderHolder.CREATOR.createFromParcel(reply)是如何实现的:
public static final Parcelable.Creator<ContentProviderHolder> CREATOR = new Parcelable.Creator<ContentProviderHolder>() { @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进行通信了。2、使用provider进行数据的查询
2.1ContentProviderProxy的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 { 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来完成。下面我们来看一下它的通信过程。2.2ContentProviderNative的onTransact
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()2.3SQLiteDataBase的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()2.4SQLiteDataBase的awQueryWithFactory()
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(); } }
在这里会生成一个driver对象,然后用此driver进行数据查询
2.5SQLiteCursorDriver的query()
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 类型的对象。
2.6CursorToBulkCursorAdaptor的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; } }
在这段代码,数据库才真正执行查询操作,并将其放入CursorWindow中。因为要返回一个BulkCursorDescriptor类型的对象,所以它会先新建一个BulkCursorDescriptor类型的对象d,然后对其进行填充。比较重要的一步是mCursor.getCount();我们来看一下它是如何执行的,这里的mCursor的类型是SQLiteCursor。2.7SQLiteCursor的getCount()
public int getCount() { if (mCount == NO_COUNT) { fillWindow(0); } return mCount; }
它会又去转调fillWindow()2.8SQLiteCursor的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,用来保存数据2.9AbstractWindowedCursor的clearOrCreateWindow()
protected void clearOrCreateWindow(String name) { if (mWindow == null) { mWindow = new CursorWindow(name); } else { mWindow.clear(); } }
关于CursorWindow是如何创建的,我们这里就不深究,它涉及到了framework层的代码。既然mWindow已经创建好了,那我们就来看一下它是如何查询并保存的数据的。2.10SQLiteQuery的fillWindow()
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什么位置开始保存requiredPos:可以用来指示出mWindow的最大容量
在此代码块中,会去调用SQLiteSession的executeForCursorWindow(),这里简单说一下SQLiteSession这个类,它是一个会话,被用来查询数据库的数据,保存了与数据库的连接,有一个连接池;并且,在每个线程中,对应一个数据库,都有一个session。它由SQLiteDataBase提供。
2.11SQLiteSession的executeForCursorWindow()
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中。
2.12BulkCursorDescriptor的writeToParcel()
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端了。3client端对数据的接收
BulkCursorDescriptor d = BulkCursorDescriptor.CREATOR.createFromParcel(reply); adaptor.initialize(d);
client端会将发过来的数据整合成一个BulkCursorDescriptor类型的对象,然后用其实例化一个BulkCursorToCursorAdaptor类型的对象adaptor,并将其作为Cursor类型的对象返回,这样,我们在client端就可以进行数据的增删该查了
- Content Provider之间互相通信的源码浅析
- Content Provider启动浅析
- 笔记-系统源码常用的Content Provider
- 浅析调用android的content provider(一)
- 浅析调用android的content provider(一)
- 浅析调用android的content provider(二)
- 浅析调用android的content provider(一)
- Android 5.0源码分析---Content Provider的启动过程分析
- Android系统源码阅读(7):Content Provider的启动
- content provider 的使用
- Android的Content Provider
- Content Provider的加载
- Content Provider的权限
- content provider的使用!
- 简单的content provider
- Content Provider的加载
- Android中的进程通信之Content Provider
- 总结Content Provider的使用
- 欧拉函数表
- Linux系统服务器上安装MySQL
- 二维数组和vector实现dijkstra
- Vue, App与我(六)
- jQuery插件开发
- Content Provider之间互相通信的源码浅析
- MySQL加强170725
- unicode,decode,encode在python的作用
- 【JavaSE系列—基础篇7】——注解基础知识
- 修改UBuntu的文件夹权限
- nginx配置与优化
- 2017.07.27am-初学Javascript的小笔记
- mybatis学习笔记
- String类——常见字符串操作指令