ContentResolver.query流程分析
创始人
2024-03-12 02:26:39
0

文章目录

      • 1.Context.getContentResolver()
      • 2.ContentResolver.query()
      • 3.ContentProviderProxy.query()
      • 4.Transport.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);}@Overrideprotected 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.** 

For best performance, the caller should follow these guidelines:**

  • 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* null if the underlying content provider returns null,* 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=nulltry {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!!!!1unstableProviderDied(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);}......@Overridepublic 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[] projectionint 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;}......      }@Overridepublic IBinder asBinder(){return this;}
    }final class ContentProviderProxy implements IContentProvider
    {public ContentProviderProxy(IBinder remote){mRemote = remote;}@Overridepublic IBinder asBinder(){return mRemote;}@Overridepublic 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;@Overridepublic 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);}}......
    }
    

    相关内容

    热门资讯

    汽车油箱结构是什么(汽车油箱结... 本篇文章极速百科给大家谈谈汽车油箱结构是什么,以及汽车油箱结构原理图解对应的知识点,希望对各位有所帮...
    美国2年期国债收益率上涨15个... 原标题:美国2年期国债收益率上涨15个基点 美国2年期国债收益率上涨15个基...
    嵌入式 ADC使用手册完整版 ... 嵌入式 ADC使用手册完整版 (188977万字)💜&#...
    重大消息战皇大厅开挂是真的吗... 您好:战皇大厅这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...
    盘点十款牵手跑胡子为什么一直... 您好:牵手跑胡子这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游...
    senator香烟多少一盒(s... 今天给各位分享senator香烟多少一盒的知识,其中也会对sevebstars香烟进行解释,如果能碰...
    终于懂了新荣耀斗牛真的有挂吗... 您好:新荣耀斗牛这款游戏可以开挂,确实是有挂的,需要了解加客服微信8435338】很多玩家在这款游戏...
    盘点十款明星麻将到底有没有挂... 您好:明星麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【5848499】很多玩家在这款游戏...
    总结文章“新道游棋牌有透视挂吗... 您好:新道游棋牌这款游戏可以开挂,确实是有挂的,需要了解加客服微信【7682267】很多玩家在这款游...
    终于懂了手机麻将到底有没有挂... 您好:手机麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...