ContentResolver.query流程分析


总结

  • 增删改查ContentProvider时,通过Binder实现
  • ContentProvider在App进程启动时进行实例化,具体时机是在Application.onCreate()执行前
  • 流程图如下:
    在这里插入图片描述

1.Context.getContentResolver()

我们知道Context实现类是ContextImpl,可以看到getContentResolver()返回的是ApplicationContentResolver,相关类图如下:
在这里插入图片描述

// frameworks\base\core\java\android\content\Context.java
public abstract ContentResolver getContentResolver();

// frameworks\base\core\java\android\app\ContextImpl.java
private final ApplicationContentResolver mContentResolver
public ContentResolver getContentResolver() {
   return mContentResolver;
}

private static final class ApplicationContentResolver extends ContentResolver {
    private final ActivityThread mMainThread;

    public ApplicationContentResolver(Context context, ActivityThread mainThread) {
        super(context);
        mMainThread = Objects.requireNonNull(mainThread);
    }

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

2.ContentResolver.query()

query()有几个重载,最后都执行了第一个,整个逻辑大致如下:从ActivityThread中缓存的mProviderMap中获取对应的ContentProvider,执行其query(),将结果包装成​CursorWrapperInner返回​
在这里插入图片描述

// frameworks\base\core\java\android\content\ContentResolver.java
/**
 * Query the given URI, returning a {@link Cursor} over the result set
 * with support for cancellation.
 *
 * <p>For best performance, the caller should follow these guidelines:
 *
 * <li>Provide an explicit projection, to prevent reading data from storage
 * that aren't going to be used.
 *
 * Provider must identify which QUERY_ARG_SORT* arguments were honored during
 * the preparation of the result set by including the respective argument keys
 * in the {@link Cursor} extras {@link Bundle}. See {@link #EXTRA_HONORED_ARGS}
 * for details.
 *
 * @see #QUERY_ARG_SORT_COLUMNS
 * @see #QUERY_ARG_SORT_DIRECTION
 * @see #QUERY_ARG_SORT_COLLATION
 *
 * @param uri The URI, using the content:// scheme, for the content to
 *         retrieve.
 * @param projection A list of which columns to return. Passing null will
 *         return all columns, which is inefficient.
 * @param queryArgs A Bundle containing additional information necessary for
 *            the operation. Arguments may include SQL style arguments, such
 *            as {@link ContentResolver#QUERY_ARG_SQL_LIMIT}, but note that
 *            the documentation for each individual provider will indicate
 *            which arguments they support.
 * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
 * If the operation is canceled, then {@link OperationCanceledException} will be thrown
 * when the query is executed.
 * @return A Cursor object, which is positioned before the first entry. May return
 *         <code>null</code> if the underlying content provider returns <code>null</code>,
 *         or if it crashes.
 * @see Cursor
 */
@Override
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
        @Nullable String[] projection, @Nullable Bundle queryArgs,
        @Nullable CancellationSignal cancellationSignal) {
    Objects.requireNonNull(uri, "uri");
    // 从ApplicationContentResolver构造方法可以看到mWrapped=null
    try {
        if (mWrapped != null) {
            return mWrapped.query(uri, projection, queryArgs, cancellationSignal);
        }
    } catch (RemoteException e) {
        return null;
    }
    // 从ActivityThread.acquireProvider()中获取,这里可以看ContentProvider启动流程
    IContentProvider unstableProvider = acquireUnstableProvider(uri);
    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(mContext.getAttributionSource(), 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);
            stableProvider = acquireProvider(uri);
            if (stableProvider == null) {
                return null;
            }
            qCursor = stableProvider.query(mContext.getAttributionSource(), 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);
        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);
        }
        if (stableProvider != null) {
            releaseProvider(stableProvider);
        }
    }
}

3.ContentProviderProxy.query()

接下来我们搜索IContentProvider的实现类,发现是ContentProviderNative和ContentProviderProxy

从这几个类命名和内容,很容易想到和平时写aidl生成的代码很相似,手写了Binder,根据binder调用套路,可以知道调用流程为

ContentProviderProxy.query() -->ContentProviderNative.onTransact() --> ContentProviderNative.query()


// frameworks\base\core\java\android\content\ContentProviderNative.java
abstract public class ContentProviderNative extends Binder implements IContentProvider {
    public ContentProviderNative()
    {
        attachInterface(this, descriptor);
    }
    ......
    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
            switch (code) {
                case QUERY_TRANSACTION:
                {
                    data.enforceInterface(IContentProvider.descriptor);

                    AttributionSource attributionSource = AttributionSource.CREATOR
                            .createFromParcel(data);
                    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(attributionSource, 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;
                }
        ......      
    }

    @Override
    public IBinder asBinder()
    {
        return this;
    }
}

final class ContentProviderProxy implements IContentProvider
{
    public ContentProviderProxy(IBinder remote)
    {
        mRemote = remote;
    }

    @Override
    public IBinder asBinder()
    {
        return mRemote;
    }

    @Override
    public Cursor query(@NonNull AttributionSource attributionSource, 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);

            attributionSource.writeToParcel(data, 0);
            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();
        }
    }

}

4.Transport.query()

接着搜索谁继承了ContentProviderNative,发现是Transport(Transport为ContentProvider内部类),其query()最终调用了ContentProvider.query(),至此整个ContentResolver.query()流程分析完毕

// frameworks\base\core\java\android\content\ContentProvider.java
/**
 * Binder object that deals with remoting.
 *
 * @hide
 */
// Transport为ContentProvider内部类
class Transport extends ContentProviderNative {
    ......
    volatile ContentInterface mInterface = ContentProvider.this;
    @Override
    public Cursor query(@NonNull AttributionSource attributionSource, Uri uri,
            @Nullable String[] projection, @Nullable Bundle queryArgs,
            @Nullable ICancellationSignal cancellationSignal) {
        uri = validateIncomingUri(uri);
        uri = maybeGetUriWithoutUserId(uri);
        if (enforceReadPermission(attributionSource, uri)
                != PermissionChecker.PERMISSION_GRANTED) {
            // 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;
            final AttributionSource original = setCallingAttributionSource(
                    attributionSource);
            try {
                cursor = mInterface.query(
                        uri, projection, queryArgs,
                        CancellationSignal.fromTransport(cancellationSignal));
            } catch (RemoteException e) {
                throw e.rethrowAsRuntimeException();
            } finally {
                setCallingAttributionSource(original);
            }
            if (cursor == null) {
                return null;
            }
            // Return an empty cursor for all columns.
            return new MatrixCursor(cursor.getColumnNames(), 0);
        }
        traceBegin(TRACE_TAG_DATABASE, "query: ", uri.getAuthority());
        final AttributionSource original = setCallingAttributionSource(
                attributionSource);
        try {
            return mInterface.query(
                    uri, projection, queryArgs,
                    CancellationSignal.fromTransport(cancellationSignal));
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        } finally {
            setCallingAttributionSource(original);
            Trace.traceEnd(TRACE_TAG_DATABASE);
        }
    }
    ......
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值