美文网首页
ContentProvider query方法的实现流程(基于a

ContentProvider query方法的实现流程(基于a

作者: android_coder | 来源:发表于2019-05-22 11:59 被阅读0次

    我们在实际项目开发中会使用到各种各样的contentprovider,包括读取通话记录,联系人信息等等,我们都知道contentprovider是跨进程通信的,一个应用访问另外一个应用的contentprovider是一个binder的过程,接下来我们就分析下这个过程

    应用层读取联系人的实现
    public static List<ContactsBean> getContactsList(Context context) {
            Log.d("SMSHelper", "-----------SMSHelper#getContactsList()----------");
            List<ContactsBean> list = new ArrayList<ContactsBean>();
            ContactsBean bean = null;
            ContentResolver resolver = context.getContentResolver();
            Cursor cursor = resolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
            if (cursor == null && cursor.getCount() <= 0) {
                return null;
            }
            Logger.d("SMSHelper", "cursor.getCount():" + cursor.getCount());
            while (cursor.moveToNext()) {
                bean = new ContactsBean();
                String name = 
                cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));//姓名
                wirteNumbers(resolver, name, bean);
                list.add(bean);
            }
            cursor.close();
            return list;
        }
    

    我们可以看到首先是获取一个contentresolver对象,然后调用这个对象的query方法去实现查询

    ContentResolver之query方法
     public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
                @Nullable String[] projection, @Nullable String selection,
                @Nullable String[] selectionArgs, @Nullable String sortOrder) {
     return query(uri, projection, selection, selectionArgs, sortOrder, null);
        }
    
    public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
                @Nullable String[] projection, @Nullable String selection,
                @Nullable String[] selectionArgs, @Nullable String sortOrder,
                @Nullable CancellationSignal cancellationSignal) {
            Bundle queryArgs = createSqlQueryBundle(selection, selectionArgs, sortOrder);
            return query(uri, projection, queryArgs, cancellationSignal);
        }
    
     public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
                @Nullable String[] projection, @Nullable Bundle queryArgs,
                @Nullable CancellationSignal cancellationSignal) {
            Preconditions.checkNotNull(uri, "uri");
            IContentProvider unstableProvider = acquireUnstableProvider(uri);---->获取unstableprovider
            if (unstableProvider == null) {
                return null;
            }
            IContentProvider stableProvider = null;
            Cursor qCursor = null;
            try {
                long startTime = SystemClock.uptimeMillis();
                ICancellationSignal remoteCancellationSignal = null;
                if (cancellationSignal != null) {
                    cancellationSignal.throwIfCanceled();
                    remoteCancellationSignal = unstableProvider.createCancellationSignal();
                    cancellationSignal.setRemote(remoteCancellationSignal);
                }
                try {
                    qCursor = unstableProvider.query(mPackageName, uri, projection,
                            queryArgs, remoteCancellationSignal);-------->执行查询操作
                } catch (DeadObjectException e) {
                    // The remote process has died...  but we only hold an unstable
                    // reference though, so we might recover!!!  Let's try!!!!
                    // This is exciting!!1!!1!!!!1
                    unstableProviderDied(unstableProvider);------------>远程的进程死亡,处理死亡的provider
                    stableProvider = acquireProvider(uri);----------------->获取stable的provider
                    if (stableProvider == null) {
                        return null;
                    }
                    qCursor = stableProvider.query(----------------------->执行查询过程
                            mPackageName, uri, projection, queryArgs, remoteCancellationSignal);
                }
                if (qCursor == null) {
                    return null;
                }
    
                // Force query execution.  Might fail and throw a runtime exception here.
                qCursor.getCount();---------------------------------------->强制执行查询过程,可能会出现失败并抛出异常
                long durationMillis = SystemClock.uptimeMillis() - startTime;------------------>记录操作时间
                maybeLogQueryToEventLog(durationMillis, uri, projection, queryArgs);
    
                // Wrap the cursor object into CursorWrapperInner object.
                final IContentProvider provider = (stableProvider != null) ? stableProvider
                        : acquireProvider(uri);
                //创建CursorWrapperInner对象
                final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);
                stableProvider = null;
                qCursor = null;
                return wrapper;
            } catch (RemoteException e) {
                // Arbitrary and not worth documenting, as Activity
                // Manager will kill this process shortly anyway.
                return null;
            } finally {
                if (qCursor != null) {
                    qCursor.close();
                }
                if (cancellationSignal != null) {
                    cancellationSignal.setRemote(null);
                }
                if (unstableProvider != null) {
                    releaseUnstableProvider(unstableProvider);---------->释放provider
                }
                if (stableProvider != null) {
                    releaseProvider(stableProvider);
                }
            }
        }
    

    上面是三个同名的方法,我们看下最后一个方法的实现
    1: IContentProvider unstableProvider = acquireUnstableProvider(uri);获取一个IContentProvider对象
    2:调用该对象的query方法
    IContentProvider是一个接口,我们找到它的实现类,我们搜索implements IContentProvider可以看到ContentProviderNative继承了Binder对象并且实现了该接口,ContentProviderProxy也实现了该接口,我们知道binder通信是c/s模式,涉及到客户端和服务端,服务端也就是被查下的contentprovider,简单的来说就是bn,bp,在这里ContentProviderProxy就是客户端
    3:stable和unstable的区别,unstable类型的provider不会因为远程服务端的provider进程死亡而被杀掉,stable则恰恰相反,对于app无法事先决定创建的ContentProvider是stable,还是unstable 类型的,也便无法得知自己的进程是否会依赖于远程ContentProvider的生死。

    ContentResolver之acquireUnstableProvider
    public final IContentProvider acquireUnstableProvider(String name) {
            if (name == null) {
                return null;
            }
            return acquireUnstableProvider(mContext, name);
        }
    
     public final IContentProvider acquireUnstableProvider(Uri uri) {
            if (!SCHEME_CONTENT.equals(uri.getScheme())) {----------------------------->content
                return null;
            }
            String auth = uri.getAuthority();
            if (auth != null) {
                return acquireUnstableProvider(mContext, uri.getAuthority());
            }
            return null;
     }
    

    contentResolver之acquireProvider

        public final IContentProvider acquireProvider(Uri uri) {
            if (!SCHEME_CONTENT.equals(uri.getScheme())) {
                return null;
            }
            final String auth = uri.getAuthority();
            if (auth != null) {
                return acquireProvider(mContext, auth);
            }
            return null;
        }
    

    contentResolver是一个抽象类,实现类是ApplicationContentResolver,不管是acquireProvider还是acquireUnstableProvider,最终调用的都是activityThread的acquireProvider方法

    ActivityThread之acquireProvider
        public final IContentProvider acquireProvider(
                Context c, String auth, int userId, boolean stable) {
            final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
            //从本地的arraymap列表中读取,如果存在直接返回,provider是四大组件中实例化最早的组件, 
            // provider的oncreate方法回调比application的oncreate方法都要早
            if (provider != null) {
                return provider;
            }
            // There is a possible race here.  Another thread may try to acquire
            // the same provider at the same time.  When this happens, we want to ensure
            // that the first one wins.
            // Note that we cannot hold the lock while acquiring and installing the
            // provider since it might take a long time to run and it could also potentially
            // be re-entrant in the case where the provider is in the same process.
            ContentProviderHolder holder = null;
            try {
                synchronized (getGetProviderLock(auth, userId)) {
                    holder = ActivityManager.getService().getContentProvider(
                            getApplicationThread(), auth, userId, stable);
                }
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
            if (holder == null) {
                Slog.e(TAG, "Failed to find provider info for " + auth);
                return null;
            }
    
            // Install provider will increment the reference count for us, and break
            // any ties in the race.
            holder = installProvider(c, holder, holder.info,
                    true /*noisy*/, holder.noReleaseNeeded, stable);
            return holder.provider;
        }
    
    ContentProviderProxy之query
    public Cursor query(String callingPkg, Uri url, @Nullable String[] projection,
                @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal)
                throws RemoteException {
            BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            try {
                data.writeInterfaceToken(IContentProvider.descriptor);
                data.writeString(callingPkg);
                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.writeBundle(queryArgs);
                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);
                    Binder.copyAllowBlocking(mRemote, (d.cursor != null) ? d.cursor.asBinder() : null);
                    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();
            }
        }
    

    客户端调用transact方法回调到服务端的ontranct方法

    ContentProviderNative
     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
                throws RemoteException {
            try {
                switch (code) {
                    case QUERY_TRANSACTION:
                    {
                        data.enforceInterface(IContentProvider.descriptor);
                        String callingPkg = data.readString();
                        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();
                            }
                        }
                        Bundle queryArgs = data.readBundle();
                        IContentObserver observer = IContentObserver.Stub.asInterface(
                                data.readStrongBinder());
                        ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
                                data.readStrongBinder());
                        Cursor cursor = query(callingPkg, url, projection, queryArgs, 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;
                    }
    

    Cursor cursor = query(callingPkg, url, projection, queryArgs, cancellationSignal);获取一个游标对象
    我们知道ContentProviderNative是一个抽象类,我们找到它的实现类,通过搜索extends ContentProviderNative,我们找到Transport

    Transport
     class Transport extends ContentProviderNative {
            AppOpsManager mAppOpsManager = null;
            int mReadOp = AppOpsManager.OP_NONE;
            int mWriteOp = AppOpsManager.OP_NONE;
            ContentProvider getContentProvider() {
                return ContentProvider.this;
            }
            @Override
            public String getProviderName() {
                return getContentProvider().getClass().getName();
            }
            @Override
            public Cursor query(String callingPkg, Uri uri, @Nullable String[] projection,
                    @Nullable Bundle queryArgs, @Nullable ICancellationSignal cancellationSignal) {
                validateIncomingUri(uri);
                uri = maybeGetUriWithoutUserId(uri);
                if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
                    // The caller has no access to the data, so return an empty cursor with
                    // the columns in the requested order. The caller may ask for an invalid
                    // column and we would not catch that but this is not a problem in practice.
                    // We do not call ContentProvider#query with a modified where clause since
                    // the implementation is not guaranteed to be backed by a SQL database, hence
                    // it may not handle properly the tautology where clause we would have created.
                    if (projection != null) {
                        return new MatrixCursor(projection, 0);
                    }
                    // Null projection means all columns but we have no idea which they are.
                    // However, the caller may be expecting to access them my index. Hence,
                    // we have to execute the query as if allowed to get a cursor with the
                    // columns. We then use the column names to return an empty cursor.
                    Cursor cursor = ContentProvider.this.query(-------------->回调contentprovider的query方法
                            uri, projection, queryArgs,
                            CancellationSignal.fromTransport(cancellationSignal));
                    if (cursor == null) {
                        return null;
                    }
                    // Return an empty cursor for all columns.
                    return new MatrixCursor(cursor.getColumnNames(), 0);
                }
                final String original = setCallingPackage(callingPkg);
                try {
                    return ContentProvider.this.query(
                            uri, projection, queryArgs,
                            CancellationSignal.fromTransport(cancellationSignal));
                } finally {
                    setCallingPackage(original);
                }
            }
    

    此时被调用的Contentprovider的query方法就会被回调,这样就完成了查下流程
    query方法是运行在binder线程中,query是一个比较耗时的操作

    相关文章

      网友评论

          本文标题:ContentProvider query方法的实现流程(基于a

          本文链接:https://www.haomeiwen.com/subject/uxuyzqtx.html