剖析ContentProvider的query操作

年终(农历年)的一篇任务文档。
本篇剖析ContentProvider的query操作,包括了以下内容:

  1. 获取ContentProvider对象
  2. Server端的Cursor对象
  3. Client端的Cursor对象
  4. CursorWindow

1. 获取ContentProvider对象

通常,我们会使用下面的方式使用ContentProvider获取数据:

mContext.getContentResolver().query(...);

首先要拿到context对象,即上面代码中的“mContext”,这个context对象可能是Activity,Service或者Application。
其次调用“getContentResolver()”方法,但是无论是Activity,Service还是Application都没有重写该方法,所以使用的依然是它们共同的父类ContextWrapper中的“getContentResolver()”方法,代码实现如下:

    @Override
    public ContentResolver getContentResolver() {
        return mBase.getContentResolver();
    }

上面代码中的“mBase”是ContextImpl对象,该对象是在Activity,Service和Application创建时被创建的。
ContextImpl.java中对getContentResolver的实现很简单:

    @Override
    public ContentResolver getContentResolver() {
        return mContentResolver;
    }

“mContentResolver”是ApplicationContentResolver对象; ApplicationContentResolver是ContextImpl中的静态内部类,继承了ContextResolver。
ContentResolver的query方法实现逻辑如下:

  • 获取Provider对象
  • 调用Provider对象的query方法
  • 对得到的cursor对象进行包装,然后将结果返回

代码如下(为了减少篇幅,删了部分代码):

    public final @Nullable Cursor query(final @NonNull Uri uri, @Nullable String[] projection,
            @Nullable String selection, @Nullable String[] selectionArgs,
            @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) {
        Preconditions.checkNotNull(uri, "uri");
        // 1.获取Provider
        IContentProvider unstableProvider = acquireUnstableProvider(uri);
        if (unstableProvider == null) {
            return null;
        }
        IContentProvider stableProvider = null;
        Cursor qCursor = null;
        try {
              ...
            try {
                //2.调用Provider的query方法
                qCursor = unstableProvider.query(mPackageName, uri, projection,
                        selection, selectionArgs, sortOrder, remoteCancellationSignal);
            } catch (DeadObjectException e) {
                ...
                unstableProviderDied(unstableProvider);
                stableProvider = acquireProvider(uri);
                if (stableProvider == null) {
                    return null;
                }
                qCursor = stableProvider.query(mPackageName, uri, projection,
                        selection, selectionArgs, sortOrder, remoteCancellationSignal);
            }
            if (qCursor == null) {
                return null;
            }
            ...
            //3.返回cursor结果
            // Wrap the cursor object into CursorWrapperInner object.
            CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
                    stableProvider != null ? stableProvider : acquireProvider(uri));
            stableProvider = null;
            qCursor = null;
            return wrapper;
        } catch (RemoteException e) {
            ...
        } finally {
            ...
            //清理释放资源
        }
    }

ApplicationContentResolver重写了ContentResolver的acquireUnstableProvider(…)方法:

        @Override
        protected IContentProvider acquireUnstableProvider(Context c, String auth) {
            return mMainThread.acquireProvider(c,
                    ContentProvider.getAuthorityWithoutUserId(auth),
                    resolveUserIdFromAuthority(auth), false);
        }

代码中的“mMainThread”是ActivityThread类型的对象,acquireProvider的代码实现如下:

    public final IContentProvider acquireProvider(
            Context c, String auth, int userId, boolean stable) {
        //查找Provider是否已经存在
        final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
        if (provider != null) {
            return provider;
        }
        ...
        IActivityManager.ContentProviderHolder holder = null;
        try {
            //通过ActivityManagerService获取Provider
            holder = ActivityManagerNative.getDefault().getContentProvider(
                    getApplicationThread(), auth, userId, stable);
        } catch (RemoteException ex) {
        }
        if (holder == null) {
            Slog.e(TAG, "Failed to find provider info for " + auth);
            return null;
        }
        ...
        //保存Provider的信息,以后使用
        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }

acquireExistingProvider(…)方法会从“mProviderMap”中查找依然active的目标Provider,如果能成功获取那么就直接使用; 否则就要通过ActivityManagerService来获取Provider。

至于ActivityManagerService的getContentProvider(…)方法是如何获取Provider的, 这里就不贴代码了,只简述相关逻辑。
首先查找provider是否已经运行,如果已经在运行,判断provider是否可以运行在多进程,如果可以,只是返回一个不包含Provider对象的ContentProviderHolder对象,让调用进程自己创建Provider对象; 否则就返回包含Provider对象的ContentProviderHolder对象。
如果Provider没有在运行,那么就创建ContentProviderRecord对象,然后判断Provider是否可以在多线程运行,如果可以就返回一个不包含Provider对象的ContentProviderHolder对象,让调用进程自己创建Provider对象; 否则就返回包含Provider对象的ContentProviderHolder对象。

对于新获取的provider,installProvider(…)方法会根据ActivityManagerService返回的结果来决定是否创建Provider对象,另外还会将相关Provider关信息存储到成员变量mLocalProviders,mProviderRefCountMap,mLocalProvidersByName和mProviderMap中。

那么ContentProviderHolder中Provider对象是什么? (两个进程的情况)

ActivityManagerService返回的是一个ContentProviderHolder对象,我们可以查看Server端ActivityManagerNative.java中的代码:

        case GET_CONTENT_PROVIDER_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            IApplicationThread app = ApplicationThreadNative.asInterface(b);
            String name = data.readString();
            int userId = data.readInt();
            boolean stable = data.readInt() != 0;
            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;
        }

我们看到从ActivityManagerService拿到ContentProviderHolder后,会调用起writeToParcel(…)方法,代码如下:

        @Override
        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);
        }

上面代码中的provider是什么?可以查看ActivityThread的installProvider(…)方法,下面这行代码揭示了provider是什么?

provider = localProvider.getIContentProvider();

“localProvider”就是ContentProvider的实例,比如ContactsProvider2; 而getIContentProvider()方法在ContentProvider.java中的实现如下:

    public IContentProvider getIContentProvider() {
        return mTransport;
    }

“mTransport”指向的是Transport对象; Transport是ContentProvider.java的内部类,继承了ContentProviderNative类(继承了Binder),这就是server端执行query操作的地方。现在我们知道ContentProviderHold往parcel中写入的其实是Transport对象。

我看再看Client端拿到Server端返回的ContentProviderHolder对象后是怎么处理的,相关代码在ActivityManagerProxy类中,实现如下:

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

这里通过ContentProviderHolder的createFromParcel方法构建了一个新的ContenProviderHolder对象,代码实现如下:

   public static final Parcelable.Creator<ContentProviderHolder> CREATOR
           = new Parcelable.Creator<ContentProviderHolder>() {
       @Override
       public ContentProviderHolder createFromParcel(Parcel source) {
           return new ContentProviderHolder(source);
       }
       ...
   };

   private ContentProviderHolder(Parcel source) {
       info = ProviderInfo.CREATOR.createFromParcel(source);
       provider = ContentProviderNative.asInterface(
               source.readStrongBinder());
       connection = source.readStrongBinder();
       noReleaseNeeded = source.readInt() != 0;
   }

ContentProviderHolder的provider成员是一个IContentProvider类型的变量,创建ContentProviderHolder对象时,provider也会赋值:

   provider = ContentProviderNative.asInterface(
           source.readStrongBinder());

provider指向的是一个ContentProviderProxy类型的对象。

经过上面的分析,我们知道对于Client端和Server端运行在两个不同进程的情况,Client端拿到的是ContentProviderProxy对象; 不过对于Client端和Server端运行在同一个进程的情况,拿到的就是Transport对象了。这整个过程就是binder通信的流程


2. Client端的Cursor对象

现在回到ContentResolver的query方法中,通过上面的分析我们知道下面代码中的unstableProvider指向的就是ContentProviderProxy对象。

   qCursor = unstableProvider.query(mPackageName, uri, projection,
           selection, selectionArgs, sortOrder, remoteCancellationSignal);

我们看一下ContentProviderProxy的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 {
            ...
            
            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) {
           ...
        } catch (RuntimeException ex) {
           ...
        } finally {
           ...
        }
    }

在这个方法的第一行就new了一个BulkCursorToCursorAdaptor对象,并定义了一个变量adaptor,指向新对象; 然后我们找return语句,发现最后返回的就是adaptor。

ContentResolver拿到ContentProviderProxy返回的结果后,又进行了包装,代码如下:

  try {
                qCursor = unstableProvider.query(mPackageName, uri, projection,
                        selection, selectionArgs, sortOrder, remoteCancellationSignal);
            } catch (DeadObjectException e) {
            ...
            }
            if (qCursor == null) {
                return null;
            }
            ...
            // Wrap the cursor object into CursorWrapperInner object.
            CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
                    stableProvider != null ? stableProvider : acquireProvider(uri));

ContentResolver返回的是一个CursorWrapperInner对象,也就是我们在App侧操作的cursor,其实就是CursorWrapperInner对象。
下面的类图展示了BulkCursorToCursorAdaptor的继承关系:
在这里插入图片描述


3. Server端的Cursor对象

ContentProvider的getIContentProvider方法返回的是ContentProvider的内部类Transport对象; Transport继承了ContentProviderNative类。Client端的请求首先会到ContentProviderNative的onTransact方法的QUERY_TRANSACTION case中,在这个case中Transport的query会被调用,Transport的query会调用ContentProvider对象的query方法执行查询操作,代码如下:

case QUERY_TRANSACTION:
    {
         ...
         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 {
                ...
         } else {
            ...
         }
         return true;
     }

以ContactProvider2为例,query返回的是SQLiteCursor对象,下面的流程图展示了SQLiteCursor的创建过程:
在这里插入图片描述
下面的图展示了类之间的继承关系:
在这里插入图片描述


4. CursorWindow

CursorWindow是什么? CursorWindow is a buffer containing multiple cursor rows.也就是说CursorWindow是一个缓存,里面存储的是从数据库中查出的一行行数据。CursorWindow初始创建的时候是可读可写的,供本进程使用,但是当要跨进程时,传给远端进程的就是一个只读的“view”;典型例子就是服务端创建一个CursorWindow,填充数据,然后供客户端读取。
上面貼的代码中有下面几行:

     adaptor = new CursorToBulkCursorAdaptor(cursor, observer,
             getProviderName());
     cursor = null;

     BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();
     adaptor = null;

CursorToBulkCursorAdaptor的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;
        }
    }

我们看到,在构造BulkCursorDescriptor对象时,Cursor的getCount方法会被调用,这是第一次调用,SQLiteCursor的mCount会等于”NO_COUNT”,这时会创建CursorWindow。CursorWindow会有native调用,对应的JNI文件是android_database_cursorwindow.cpp, lib文件是CursorWindow.cpp。CursorWindow.cpp会创建一块匿名共享内存,并做好mmap,共享内存块的大小可以在”framework/base/core/res/res/values/config.xml“的config_cursorWindowSize字段中进行配置,默认是2048KB; 这块共享内存就是用来存储从DB中查询出的内容。
下面的图展示了上述内容,Client 端的CursorWindow是通过 createFromParcel(Parcel source) 方法创建的,Server端的内容是放入Parcel中通过Binder传递到Client端的。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值