zoukankan      html  css  js  c++  java
  • Android Cursor浅析

    1. 本文目的

    Android ContentProvider提供了进程间数据交换的一种机制。而数据库的查询就是这样的机制的应用。那么app通过Uri查询数据库而得到的Cursor到底是个什么东西?为何能够为我们提供还有一个进程的数据?本文以getContentResolver().query(……)函数为起点。全面分析Cursor家族关系类图,理清Cursor跨进程通信的机制。


    1.1 client的Cursor对象

    如果B进程中有一个ContentProvider。A进程通过Uri查询这个ContentProvider,从而得到一个Cursor。可能的代码例如以下:

    ContentResolver cr = mContext.getContentResolver();//mContext是一个Context对象
    Cursor cs = cr.query(uri,null,null,null,null);

    为了知道上述代码得到的Cursor的真实面貌,我们须要看下上述query的调用途径。query函数的实现例如以下:

        public final Cursor query(final Uri uri, String[] projection,
                String selection, String[] selectionArgs, String sortOrder,
                CancellationSignal cancellationSignal) {
            IContentProvider unstableProvider = acquireUnstableProvider(uri);
            //省略无关代码
            try {
                //省略无关代码
                Cursor qCursor;
                try {
                    qCursor = unstableProvider.query(uri, projection,
                            selection, selectionArgs, sortOrder, remoteCancellationSignal);
                } catch (DeadObjectException e) {
                  //省略无关代码
                }
                if (qCursor == null) {
                    return null;
                }
               //省略无关代码
                CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
                        stableProvider != null ?

    stableProvider : acquireProvider(uri)); stableProvider = null; return wrapper; } catch (RemoteException e) { //省略无关代码 } finally { //省略无关代码 } }


    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    为了方便分析,省略了无关代码。从以上代码能够看出:
    

    1.通过query函数返回的Cursor是一个CursorWrapperInner对象。

    2.CursorWrapperInner是一个包装类。是通过以IContentProvider 的query函数返回的Cursor对象构建的。

    那么有两个疑问:

    1.IContentProvider 的query函数返回的Cursor。真实对象是?

    2.CursorWrapperInner类起什么作用,为什么要把IContentProvider 的query函数返回的Cursor作为參数,从新构造一个CursorWrapperInner?

    首先来看下类图:

    从上述类图能够得知:

    CursorWrapperInner是ContentResolver的内部类,继承自CrossProcessCursorWrapper。CrossProcessCursorWrapper从名字上看是一个实现了跨进程通信的Cursor包装类。

    从类图上也验证了这一点,CrossProcessCursorWrapper继承自CursorWrapper,实现了CrossProcessCursor接口。

    而CursorWrapper是一个包装类,它实现了Cursor接口的全部功能,它内部含有一个mCursor成员变量指向一个Cursor接口,从而能够得知。这是一个代理模式。CursorWrapper托付它内部的mCursor对象来实现全部的Cursor功能接口。

    CrossProcessCursor继承自Cursor。它主要是用于跨进程通信。

    综上,眼下我们能够知道。CrossProcesCursorWrapper实现了所有的Cursor接口,可是这些接口功能的完毕所有托付它父类内部的mCursor来完毕。那么mCursor的真实对象是什么呢?暂且猜測。mCursor应该是一个实现了CrossProcessCursor的对象。

    总结:client拿到的Cursor的真实对象是:CursorWarpprtInner类。

    1.2 CursorWrapper内部的Cursor真实面目

    从上节我们已经知道。通过代理模式,CursorWrapperInner终于会托付CursorWrapper来完毕实际功能。如今就看看CursorWrapper内部的mCursor的真实面目。mCursor来自于IContentProvider 的query函数所返回的Cursor对象。那么这个Cursor对象是啥呢?那就要看看IContentProvider的query函数的实现了。IContentProvider的实际上是一个ContentProviderProxy对象。

    它是一个代理类。也是一个Binder的Bp端,将函数调用的參数打包发送给ContentProviderNative,终于由ContentProviderNative把来调用ContentProvider的详细函数。

    ContentProviderProxy,ContentProviderNative,ContentProvider。IContentProvider的关系例如以下:

    以下是ContentProviderProxy的query实现:

        public Cursor query(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);
    
                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(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;
            } catch (RemoteException ex) {
                adaptor.close();
                throw ex;
            } catch (RuntimeException ex) {
                adaptor.close();
                throw ex;
            } finally {
                data.recycle();
                reply.recycle();
            }
        }
    从上面代码能够得之,query函数返回的Cursor的真实对象是BulkCursorToCursorAdaptor。在query函数内部通过transact函数把query请求传递到ContentProviderNative端。

    transact运行完成后会返回一个Parcel reply。从reply中构造一个BulkCursorDescriptor。然后由这个BulkCursorDescriptor初始化BulkCursorToCursorAdaptor。

    BulkCursorToCursorAdaptor中一个相当重要的赋值操作例如以下:

        public void initialize(BulkCursorDescriptor d) {
            mBulkCursor = d.cursor;
            mColumns = d.columnNames;
            mRowIdColumnIndex = DatabaseUtils.findRowIdColumnIndex(mColumns);
            mWantsAllOnMoveCalls = d.wantsAllOnMoveCalls;
            mCount = d.count;
            if (d.window != null) {
                setWindow(d.window);
            }
        }

    d.window是一个CursorWindow对象。这个对象实际上代表着一块共享内存,存储着查询后的结果集。详情请參见:http://blog.csdn.net/ifloveelse/article/details/28394103。而mBulkCursor是一个IBulkCursor接口。这个接口起到传输数据的作用。分析到这一步,Cursor的”全家幅“更加的具体了:


    总结:在本小节开头就提出的问题:CursorWrapper内部的Cursor真实面目是谁?如今也能够解答了:BulkCursorToCursorAdaptor。

    从名字上看这是个适配器。将Cursor的功能接口通过转换,调用IBulkCursor来实现。自此。上面的这个类图就是APP端进程中Cursor的所有关系了。那么IBulkCursor又是做什么的呢?下一小节解说。

    1.3 IBulkCursor家族

    BulkCursorToCursorAdaptor的mBulkCursor来自于ContentProviderNative的返回值。为了弄清楚IBulkCursor的真实面貌。还要去看看ContentProviderNative的实现。

        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());
                        ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
                                data.readStrongBinder());
    
                        Cursor cursor = query(url, projection, selection, selectionArgs, sortOrder,
                                cancellationSignal);
                        if (cursor != null) {
                            CursorToBulkCursorAdaptor adaptor = new CursorToBulkCursorAdaptor(
                                    cursor, observer, getProviderName());
                            BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();
    
                            reply.writeNoException();
                            reply.writeInt(1);
                            d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                        } else {
                            reply.writeNoException();
                            reply.writeInt(0);
                        }
    
                        return true;
                    }
                }
    …………
    

    上面的代码是ContentProviderNative中onTransact函数对query的处理。

    在这部分代码中,又冒出了个CursorToBulkCursorAdaptor对象。这个对象的构造函数是一个Cursor对象。那么这个Cursor对象的又是谁呢?逻辑越来越复杂了。Cursor是由query函数返回。

    由1.2节中所提供的ContentProvider的类图能够得之,这个query调用的是ContentProvider的query函数。那么ContentProvider的query函数中的Cursor又是哪里来的呢?这里直接给出答案:SQLiteDatabase。ContentProvider是一个抽象类,我们须要自己实现当中的query函数。一般,在query中,我们通过SQLiteDatabase查询自己定义的数据库,得到一个Cursor对象。这个过程就省略。我们须要知道的是:SQLiteDatabase的query函数返回一个Cursor。这个Cursor用来构建了一个CursorToBulkCursorAdaptor对象。

    以下就看看SQLiteDatabase返回的Cursor的真实面貌。以下是SQLiteDatabase中query的终于调用函数。

    详细的代码能够參考SQLiteDatabase.java文件:

        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(); } }

    由上面的代码能够得之,Cursor来自于SQLiteDirectCursorDriver的query。那最好还是在看看其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;
        }

    这里我们如果factory为空。那么此处的Cursor最终露出了它的真实面貌:SQLiteCursor。 一路跟踪的真是辛苦,水落石出了!

    通过以下的类图我们整理下我们的思路:


    从图中能够看出BulkCursorToCursorAdaptor的成员变量的mBulkCursor是一个IBuilCursor接口,它的真实对象事实上是一个BulkCursorProxy。

    BulkCursorProxy是一个代理端,也是一个Bp端,通过Binder通信把函数调用请求转发给还有一个进程中的Bn端BulkCursorNative。

    图中。绿线方框部分执行在app进程中,红线方框部分执行在ContentProvider进程中。

    CursorToBulkCursorAdaptor中的mCursor的真实对象也揭晓了:SQLiteCursor。

    从名字上看也知道这个对象和SQLite有关系了。它内部有一个SQLiteQuery。负责数据库的查询和CursorWindow的建立。红线和绿线方框的交叉处CursorWindow是共享内存的抽象。在两个进程中都存在一份映射。

    自此。Cursor的分析所有结束。


  • 相关阅读:
    JSP学习笔记
    Java之String、StringBuffer、StringBuilder的区别
    Android开发笔记——Handler总结
    深搜_八皇后(HDU_2553)
    广搜_优先队列和记录搜索路径(HDU_1026)
    深搜_素数环(HDU_1016)
    深搜_奇偶减枝(HDU_1010)
    转载 ASP.NET MVC学习之(5):Html.ActionLink
    启动万维网发布服务(W3SVC)
    What is POID
  • 原文地址:https://www.cnblogs.com/mfmdaoyou/p/6892409.html
Copyright © 2011-2022 走看看