← 返回文章流

Android源码分析: ContentProvider查询以及数据变化监听分析

之前已经分析了启动应用安装ContentProvider,使用时获取ContentProvider,我们这里再分析一下使用ContentProvider查询数据已经监听ContentProvider数据变化的情况。 查询数据 上次的文章已经介绍了使用query的方法,并且已经介绍完了通过acquireProvider获取到ContentProvider,如果是是本地应用的话拿到的是Transport对象,如果是查询其他应用(不严谨的说法,其他应用也要排查userId不同,且不共享签名),则拿到的是ContentProviderProxy,这里我们要分析的查询是其他应用的情况,因此我们需要关注ContentProviderProxy的query方法。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 @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(); } } 这个代码比较简单,把需要查询的条件写入到Parcel中,然后通过mRemote进行binder调用,在reply中拿到远端执行的结果。如果执行成功了,则通过BulkCursorDescriptor来读取reply中的数据,主要是拿到了其中IBulkCursor的Binder对象和CursorWindow这个对象。在查询的流程中会涉及到很多的类,我这里画了使用端和服务端会使用到的Cursor所涉及到相关类和接口。 其中BulkCursorToCursorAdapter为客户端使用,用于读取服务端通过binder传过来的数据,其中的封装和使用,我们后面还会继续看到。关于服务端的我们先继续往后看代码,随后会涉及到相关的类。 可以看看Provider服务端是如何把这些东西放到reply中的。我们这个时候可以看一下ContentProviderNative的onTransact的QUERY_TRANSACTION的这一分支: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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()); 这是其中的第一部分代码,就是把binder传过来的查询需要的数据进行反序列化。 1 2 Cursor cursor = query(attributionSource, url, projection, queryArgs, cancellationSignal); 第二部分为调用query进行查询,我们知道在数据提供端,其实是Transport,可以看看它的query方法。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 @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) { if (projection != null) { return new MatrixCursor(projection, 0); } 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); } } 其中的代码也是比较简单的,首先调用enforceReadPermission检查是否有使用这个ContentProvider的权限,如果有权限则调用mInterface.query,我们看源码就知道,这个mInterface也就是一个ContentProvider,也就是我们开发过程实现的那个ContentProvider,query方法也就是我们自己的实现。 我们先不着急分析后面的代码,我们前面说过如果是本进程的ContentProvider查询会直接调用Transport的query方法,那么就不存在binder调用,而是直接调用了我们所实现的query。这个实现还是很妙的,值得我们学习。对于我们跨进程的调用,还需要看ContentProviderNative的onTransact后面的代码,也就是我们要说的第三部分: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 2

来自 码农明明桑作者主页
在原博客阅读全文 (新标签页打开)

以上内容来自博客订阅源,版权归原作者。关注作者后可以在 关注列表 第一时间查看作者文章。

一起聊聊 0

尊重不同观点

还没有评论。欢迎分享第一个想法。

RSS SUBSCRIPTIONS

选择 RSS 订阅

把博客汇的文章添加到你常用的阅读器。

关注文章 RSS 订阅本订阅需要登录后使用