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端就可以进行数据的增删该查了


原创粉丝点击