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

2019-05-22  本文已影响0人  android_coder

我们在实际项目开发中会使用到各种各样的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是一个比较耗时的操作

上一篇下一篇

猜你喜欢

热点阅读