文章目录
- 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);
}
@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);
}
}
......
}