ContentProvider是一种内容共享型组件,它通过Binder向其它组件乃至其它应用提供数据。关于ContentProvider是如何使用的,我们在之前文章《Android里内容提供者ContentProvider的使用》中已经有介绍过和实例演示。今天主要是对ContentProvider的一些工作过程作分析和学习。
一般来说,ContentProvider都应该是单实例的。因为android:multiprocess默认是false,当android:multiprocess为true时,ContentProvider为多实例,这时每个调用者的进程都存在一个ContentProvider对象。但由于实际开发中,并未发现多实例的具体使用场景(虽然官方解释说可以避免进程问通信的开销),因此我们可以简单认为ContentProvider都是单实例。下面来看看单实例的ContentProvider的启动过程。
当ContentProvider所在的进程启动时,ContentProvider会同时启动并被发布到ActivityManagerService中。而且,ContentProvider的onCreate很特殊,会先于Application的onCreate执行。我们在前面文章《Android应用程序启动详解(二)从源码了解App的启动过程》中也曾经一句话提过到这点,来回顾一下源码:
ActivityThread.java
private void handleBindApplication(AppBindData data) {
……
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
try {
// 关键代码1
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
// 关键代码2
installContentProviders(app, data.providers);
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
}
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
throw new RuntimeException( "Exception thrown in onCreate() of "
+ data.instrumentationName + ": " + e.toString(), e);
}
try {
// 关键代码3
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
} finally {
StrictMode.setThreadPolicy(savedPolicy);
}
}
关键代码1中就是创建Application对象,而关键代码3就是在创建出Application对象后,调用了callApplicationOnCreate(app)方法,也就是回调Application的onCreate方法。我们重要来看看关键代码2,它就是ContentProvider的创建过程逻辑。继续跟踪installContentProviders方法看它做了啥事情:
private void installContentProviders(
Context context, List providers) {
final ArrayList results =
new ArrayList();
// 关键代码1
for (ProviderInfo cpi : providers) {
if (DEBUG_PROVIDER) {
StringBuilder buf = new StringBuilder(128);
buf.append("Pub ");
buf.append(cpi.authority);
buf.append(": ");
buf.append(cpi.name);
Log.i(TAG, buf.toString());
}
// 关键代码2
IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
// 关键代码3
ActivityManagerNative.getDefault().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
方法内逻辑很清楚,关键代码1中遍历当前进程的ProviderInfo的列表并一一调用关键代码2中的installProvider方法来启动它们,最后就是关键代码3中,将已经启动的ContentProvider发布到ActivityManagerService中(关于ActivityManagerNative.getDefault()为什么是ActivityManagerService,我们在前面也提到很多次,不明白的话可以查看之前的文章《Android应用程序启动详解(一)》)。来看看关键代码2的installProvider方法:
private IActivityManager.ContentProviderHolder installProvider(Context context,
IActivityManager.ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
……
try {
// 关键代码1
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();
if (provider == null) {
Slog.e(TAG, "Failed to instantiate class " +
info.name + " from sourceDir " +
info.applicationInfo.sourceDir);
return null;
}
if (DEBUG_PROVIDER) Slog.v(
TAG, "Instantiating local provider " + info.name);
// XXX Need to create the correct context for this provider.
// 关键代码2
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
……;
}
} else {
……
}
IActivityManager.ContentProviderHolder retHolder;
synchronized (mProviderMap) {
if (DEBUG_PROVIDER) Slog.v(TAG, "Checking to add " + provider
+ " / " + info.name);
IBinder jBinder = provider.asBinder();
if (localProvider != null) {
……
} else {
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
……
} else {
// 关键代码3
ProviderClientRecord client = installProviderAuthoritiesLocked(
provider, localProvider, holder);
……
}
retHolder = prc.holder;
}
}
return retHolder;
}
上述关键代码1中,通过类加载器完成了ContentProvider对象的创建,还通过关键代码2中attachInfo方法来设置一些信息,来看看attachInfo的方法代码:
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
mNoPerms = testing;
/*
* Only allow it to be set once, so after the content service gives
* this to us clients can't change it.
*/
if (mContext == null) {
mContext = context;
if (context != null) {
mTransport.mAppOpsManager = (AppOpsManager) context.getSystemService(
Context.APP_OPS_SERVICE);
}
mMyUid = Process.myUid();
if (info != null) {
setReadPermission(info.readPermission);
setWritePermission(info.writePermission);
setPathPermissions(info.pathPermissions);
mExported = info.exported;
mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
setAuthorities(info.authority);
}
ContentProvider.this.onCreate();
}
}
方法中可以看到ContentProvider的onCreate方法就是在这里被调用的了,
再看回installProvider方法的关键代码3的installProviderAuthoritiesLocked方法是做了什么事情:
final ArrayMap mProviderMap = new ArrayMap();
private ProviderClientRecord installProviderAuthoritiesLocked(IContentProvider provider,
ContentProvider localProvider, IActivityManager.ContentProviderHolder holder) {
final String auths[] = holder.info.authority.split(";");
final int userId = UserHandle.getUserId(holder.info.applicationInfo.uid);
final ProviderClientRecord pcr = new ProviderClientRecord(
auths, provider, localProvider, holder);
for (String auth : auths) {
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord existing = mProviderMap.get(key);
if (existing != null) {
Slog.w(TAG, "Content provider " + pcr.mHolder.info.name
+ " already published as " + auth);
} else {
// 关键代码
mProviderMap.put(key, pcr);
}
}
return pcr;
}
原来是将刚创建的IContentProvider对象通过Map的形式来存储起来。好了,到此为止,ContentProvider就已经启动完成。
当ContentProvider启动后,外界就可以通过它所提供的insert、delete、update 和 query这四个接口来对ContentProvider中的数据源进行增删改查操作。这四个方法都是通过Binder来调用,外界无法直接访问ContentProvider,它只能通过ActivityManagerService根据Uri来获取对应用ContentProvider的Binder接口IConentProvider,然后再通过IContentProvider来访问ContentProvider中的数据源。
四个操作接口逻辑很类似,我们就从query方法入手,先来看下实例代码:
Cursor cursor = getContentResolver().query(Uri.parse("content:// com.zyx.PersonProvider/person/1"), null, null, null, null);
访问ContentProvider需要通过getContentResolver方法来获取ContentResolver的对象,ContentResolver是一个抽象类,getContentResolver方法实际上是返回了ApplicationContentResolver对象,来看代码:
ContentResolver.java
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder,
@Nullable CancellationSignal cancellationSignal) {
Preconditions.checkNotNull(uri, "uri");
// 关键代码1
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 {
// 关键代码2
qCursor = unstableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
} catch (DeadObjectException e) {
unstableProviderDied(unstableProvider);
// 关键代码3
stableProvider = acquireProvider(uri);
if (stableProvider == null) {
return null;
}
// 关键代码4
qCursor = stableProvider.query(mPackageName, uri, projection,
selection, selectionArgs, sortOrder, remoteCancellationSignal);
}
……
return wrapper;
} catch (RemoteException e) {
return null;
} finally {
……
}
}
上述代码中,关键代码1 和 关键代码3都是为了获取一个IContentProvider接口对象,分别调用了acquireUnstableProvider 和 acquireProvider 方法,然后再调用其获取对象的query方法,acquireUnstableProvider方法的内部也是调用了acquireProvider方法,ApplicationContentResolver继承ContentResolver:
ContextImpl.java
private static final class ApplicationContentResolver extends ContentResolver {
……
@Override
protected IContentProvider acquireProvider(Context context, String auth) {
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
……
}
再来看看acquireProvider方法的代码:
ActivityThread.java
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
// 关键代码1
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
IActivityManager.ContentProviderHolder holder = null;
try {
// 关键代码2
holder = ActivityManagerNative.getDefault().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;
}
关键代码1中,通过acquireExistingProvider方法来获得已经存在的ContentProvider,如果存在就直接返回。否则,就会到关键代码2处发送一个进程间请求给ActivityManagerService让其启动目标ContentProvider,这样最终就会到了进程的启动入口方法ActivityThread的main方法,通过一系列过程后就又了”ContentProvider的启动过程”中所述的installContentProviders方法去。我们来看看关键代码1 的acquireExistingProvider方法的代码:
public final IContentProvider acquireExistingProvider(
Context c, String auth, int userId, boolean stable) {
synchronized (mProviderMap) {
final ProviderKey key = new ProviderKey(auth, userId);
final ProviderClientRecord pr = mProviderMap.get(key);
if (pr == null) {
return null;
}
IContentProvider provider = pr.mProvider;
IBinder jBinder = provider.asBinder();
if (!jBinder.isBinderAlive()) {
……
return null;
}
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
incProviderRefLocked(prc, stable);
}
return provider;
}
}
我们看到了熟悉的mProviderMap对象,也就是在启动过程中调用了onCreate后将其保存起来的Map对象。知道了IContentProvider的获取,现在回到ContentResolver的query中的关键代码2和关键代码4中去看它们调用了。
IContentProvider是ContentProvider的Binder对象,它的具本实现是ContentProviderNative和ContentProvider.Transport,其中ContentProvider.Transport继承了ContentProviderNative。来看看ContentProvider.Transport的query方法代码:
ContentProvider.java
class Transport extends ContentProviderNative {
……
@Override
public Cursor query(String callingPkg, Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder,
ICancellationSignal cancellationSignal) {
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceReadPermission(callingPkg, uri, null) != AppOpsManager.MODE_ALLOWED) {
if (projection != null) {
return new MatrixCursor(projection, 0);
}
Cursor cursor = ContentProvider.this.query(uri, projection, selection,
selectionArgs, sortOrder, CancellationSignal.fromTransport(
cancellationSignal));
if (cursor == null) {
return null;
}
return new MatrixCursor(cursor.getColumnNames(), 0);
}
final String original = setCallingPackage(callingPkg);
try {
// 关键代码
return ContentProvider.this.query(
uri, projection, selection, selectionArgs, sortOrder,
CancellationSignal.fromTransport(cancellationSignal));
} finally {
setCallingPackage(original);
}
}
……
}
关键代码明显不过,它调用了ContentProvider的query方法,也就是我们自定义继承ContentProvider的类的query方法,query方法的执行结果再通过Binder返回给调用者,就这样整个调用过程就完成了。