ContentProvider 主要用于进程间和进程内数据的提供。相对于其他三大组件用的较少。
一般访问ContentProvider 都是通过 本地的ContentResolver去访问 简单的使用如下
- 从 ContextImple 获取ContentResolver
- 设置符合ContentProvider authorities的Uri
- 利用 ContentResolver 执行CRUD操作
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://com.example.OrderContentProvider/person");
ContentValues contentValue = new ContentValues();
contentValue.put("name", "许");
resolver.insert(uri, contentValue);
那么重点是怎么获取的ContentResolver 是什么,ContentResolver是为什么能够操作 ContentProvider。
1. ContextImpl.getContentResolver()
ContextImpl 返回 ContentResolver 是他的内部类 ApplicationContentResolver,并在 ContextImpl 实例化的时候一起初始化了
private final ApplicationContentResolver mContentResolver;
private ContextImpl(ContextImpl container, ActivityThread mainThread,
LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
Display display, Configuration overrideConfiguration) {
。。。。。
mContentResolver = new ApplicationContentResolver(this, mainThread, user);
}
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
2. 举例 ApplicationContentResolver.insert()
1. acquireProvider(url) 获取 IContentProvider
2.IContentProvider 直接调用 insert
按照惯例 可以猜测 获取到 的 IContentProvider 肯定也是一个 Bind 并且本身可能是一个 代理对象 代理着真实的 ContentProvider,或者直接就是 ContentProvider的内部类可以直接调用ContentProvider 的方法。
public final Uri insert(Uri url, ContentValues values)
{
IContentProvider provider = acquireProvider(url);
if (provider == null) {
throw new IllegalArgumentException("Unknown URL " + url);
}
try {
long startTime = SystemClock.uptimeMillis();
Uri createdRow = provider.insert(mPackageName, url, values);
long durationMillis = SystemClock.uptimeMillis() - startTime;
maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */);
return createdRow;
} catch (RemoteException e) {
// Arbitrary and not worth documenting, as Activity
// Manager will kill this process shortly anyway.
return null;
} finally {
releaseProvider(provider);
}
}
3. ApplicationContentResolver.acquireProvider(url)
调用了 ActivityThread的 acquireProvider 并传入了 context ,authorities,userId
@Override
protected IContentProvider acquireProvider(Context context, String auth) {
return mMainThread.acquireProvider(context,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), true);
}
4.ActivityThread.acquireProvider
这个方法分两部分
- .acquireExistingProvider 找缓存如果之前有访问过会缓存下来。
- 缓存找不到 就去找ActivityManagerService要
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
IActivityManager.ContentProviderHolder holder = null;
try {
holder = ActivityManagerNative.getDefault().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
}
if (holder == null) {
Slog.e(TAG, "Failed to find provider info for " + auth);
return null;
}
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
5.ActivityThread.acquireExistingProvider
- 根据 auth, userId 创建 ProviderKey,利用ProviderKey 从 mProviderMap 里面获取 ProviderClientRecord
- 如果ProviderClientRecord 不为空 获取 ProviderClientRecord 内的IContentProvider 。并判断 IContentProvider 是否还存活如果没有存活直接返回 null,如果还存活直接返回 IContentProvider
- 其他情况的直接返回 null
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();
//查看 IContentProvider 是否还存活着 如果不在了 那么 就返回空
if (!jBinder.isBinderAlive()) {
// The hosting process of the provider has died; we can't
// use this one.
Log.i(TAG, "Acquiring provider " + auth + " for user " + userId
+ ": existing object's process dead");
//通知ActivityManagerService ContentProvide 已经挂了
handleUnstableProviderDiedLocked(jBinder, true);
return null;
}
// Only increment the ref count if we have one. If we don't then the
// provider is not reference counted and never needs to be released.
ProviderRefCount prc = mProviderRefCountMap.get(jBinder);
if (prc != null) {
//引用计数
incProviderRefLocked(prc, stable);
}
return provider;
}
}
6.ActivityManagerNative.getDefault().getContentProvider
又调用 getContentProviderImpl
@Override
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String name, int userId, boolean stable) {
enforceNotIsolatedCaller("getContentProvider");
if (caller == null) {
String msg = "null IApplicationThread when getting content provider "
+ name;
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
// The incoming user check is now handled in checkContentProviderPermissionLocked() to deal
// with cross-user grant.
return getContentProviderImpl(caller, name, null, stable, userId);
}
7.ActivityManagerService.
private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
synchronized(this) {
long startTime = SystemClock.elapsedRealtime();
ProcessRecord r = null;
if (caller != null) {
r = getRecordForAppLocked(caller);
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when getting content provider " + name);
}
}
boolean checkCrossUser = true;
checkTime(startTime, "getContentProviderImpl: getProviderByName");
// First check if this content provider has been published...
cpr = mProviderMap.getProviderByName(name, userId);
// If that didn't work, check if it exists for user 0 and then
// verify that it's a singleton provider before using it.
if (cpr == null && userId != UserHandle.USER_OWNER) {
cpr = mProviderMap.getProviderByName(name, UserHandle.USER_OWNER);
if (cpr != null) {
cpi = cpr.info;
if (isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
&& isValidSingletonCall(r.uid, cpi.applicationInfo.uid)) {
userId = UserHandle.USER_OWNER;
checkCrossUser = false;
} else {
cpr = null;
cpi = null;
}
}
}
//之前内容提供者已经运行了
boolean providerRunning = cpr != null;
。。。。
boolean singleton;
if (!providerRunning) {
try {
checkTime(startTime, "getContentProviderImpl: before resolveContentProvider");
// 从 PackageManagerService 中找寻符合的 内容
cpi = AppGlobals.getPackageManager().
resolveContentProvider(name,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS, userId);
checkTime(startTime, "getContentProviderImpl: after resolveContentProvider");
} catch (RemoteException ex) {
}
if (cpi == null) {
return null;
}
// If the provider is a singleton AND
// (it's a call within the same user || the provider is a
// privileged app)
// Then allow connecting to the singleton provider
singleton = isSingleton(cpi.processName, cpi.applicationInfo,
cpi.name, cpi.flags)
&& isValidSingletonCall(r.uid, cpi.applicationInfo.uid);
if (singleton) {
userId = UserHandle.USER_OWNER;
}
cpi.applicationInfo = getAppInfoForUser(cpi.applicationInfo, userId);
checkTime(startTime, "getContentProviderImpl: got app info for user");
String msg;
checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");
//检查1权限
if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
!= null) {
throw new SecurityException(msg);
}
checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission");
if (!mProcessesReady && !mDidUpdate && !mWaitingUpdate
&& !cpi.processName.equals("system")) {
// If this content provider does not run in the system
// process, and the system is not yet ready to run other
// processes, then fail fast instead of hanging.
throw new IllegalArgumentException(
"Attempt to launch content provider before system ready");
}
// Make sure that the user who owns this provider is running. If not,
// we don't want to allow it to run.
if (!isUserRunningLocked(userId, false)) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
+ name + ": user " + userId + " is stopped");
return null;
}
ComponentName comp = new ComponentName(cpi.packageName, cpi.name);
checkTime(startTime, "getContentProviderImpl: before getProviderByClass");
cpr = mProviderMap.getProviderByClass(comp, userId);
checkTime(startTime, "getContentProviderImpl: after getProviderByClass");
final boolean firstClass = cpr == null;
if (firstClass) {
final long ident = Binder.clearCallingIdentity();
try {
checkTime(startTime, "getContentProviderImpl: before getApplicationInfo");
PackageManagerService
ApplicationInfo ai =s
AppGlobals.getPackageManager().
getApplicationInfo(
cpi.applicationInfo.packageName,
STOCK_PM_FLAGS, userId);
checkTime(startTime, "getContentProviderImpl: after getApplicationInfo");
if (ai == null) {
Slog.w(TAG, "No package info for content provider "
+ cpi.name);
return null;
}
ai = getAppInfoForUser(ai, userId);
cpr = new ContentProviderRecord(this, cpi, ai, comp, singleton);
} catch (RemoteException ex) {
// pm is in same process, this will never happen.
} finally {
Binder.restoreCallingIdentity(ident);
}
}
checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord");
if (r != null && cpr.canRunHere(r)) {
// If this is a multiprocess provider, then just return its
// info and allow the caller to instantiate it. Only do
// this if the provider is the same user as the caller's
// process, or can run as root (so can be in any process).
return cpr.newHolder(null);
}
if (DEBUG_PROVIDER) {
RuntimeException e = new RuntimeException("here");
Slog.w(TAG, "LAUNCHING REMOTE PROVIDER (myuid " + (r != null ? r.uid : null)
+ " pruid " + cpr.appInfo.uid + "): " + cpr.info.name, e);
}
// This is single process, and our app is now connecting to it.
// See if we are already in the process of launching this
// provider.
final int N = mLaunchingProviders.size();
int i;
for (i=0; i<N; i++) {
if (mLaunchingProviders.get(i) == cpr) {
break;
}
}
// If the provider is not already being launched, then get it
// started.
// 没有记录下来
if (i >= N) {
final long origId = Binder.clearCallingIdentity();
try {
// Content provider is now in use, its package can't be stopped.
try {
checkTime(startTime, "getContentProviderImpl: before set stopped state");
AppGlobals.getPackageManager().setPackageStoppedState(
cpr.appInfo.packageName, false, userId);
checkTime(startTime, "getContentProviderImpl: after set stopped state");
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ cpr.appInfo.packageName + ": " + e);
}
// Use existing process if already started
checkTime(startTime, "getContentProviderImpl: looking for process record");
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
内容提供者所属的进程 先开启进程 再创建
if (proc != null && proc.thread != null) {
if (DEBUG_PROVIDER) {
Slog.d(TAG, "Installing in existing process " + proc);
}
checkTime(startTime, "getContentProviderImpl: scheduling install");
proc.pubProviders.put(cpi.name, cpr);
try {
//内容提供者所属的进程存在,但是内容提供者没有启动过没有记录。所以启动下内容提供者
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
} else {
checkTime(startTime, "getContentProviderImpl: before start process");
// 内容提供者所属的进程没有开启 ,则先开启 所属进程
proc = startProcessLocked(cpi.processName,
cpr.appInfo, false, 0, "content provider",
new ComponentName(cpi.applicationInfo.packageName,
cpi.name), false, false, false);
checkTime(startTime, "getContentProviderImpl: after start process");
if (proc == null) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
+ name + ": process is bad");
return null;
}
}
cpr.launchingApp = proc;
//内容提供者 已经开启了,或正在开启
mLaunchingProviders.add(cpr);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
checkTime(startTime, "getContentProviderImpl: updating data structures");
// Make sure the provider is published (the same provider class
// may be published under multiple names).
if (firstClass) {
//第一次创建 成功 记录下来 下次复用
mProviderMap.putProviderByClass(comp, cpr);
}
mProviderMap.putProviderByName(name, cpr);
conn = incProviderCountLocked(r, cpr, token, stable);
if (conn != null) {
conn.waiting = true;
}
}
checkTime(startTime, "getContentProviderImpl: done!");
}
// Wait for the provider to be published...
synchronized (cpr) {
while (cpr.provider == null) {
if (cpr.launchingApp == null) {
Slog.w(TAG, "Unable to launch app "
+ cpi.applicationInfo.packageName + "/"
+ cpi.applicationInfo.uid + " for provider "
+ name + ": launching app became null");
EventLog.writeEvent(EventLogTags.AM_PROVIDER_LOST_PROCESS,
UserHandle.getUserId(cpi.applicationInfo.uid),
cpi.applicationInfo.packageName,
cpi.applicationInfo.uid, name);
return null;
}
try {
if (DEBUG_MU) {
Slog.v(TAG_MU, "Waiting to start provider " + cpr + " launchingApp="
+ cpr.launchingApp);
}
if (conn != null) {
conn.waiting = true;
}
cpr.wait();
} catch (InterruptedException ex) {
} finally {
if (conn != null) {
conn.waiting = false;
}
}
}
}
return cpr != null ? cpr.newHolder(conn) : null;
}
这个方法 通过 auth userId 获取 缓存的ContentProviderRecond ,以此来判断是个应该创建。这部分主要解析 ContentProvider没有被启用过的过程.
- 从 PackageService 找到符合条件的 ContentProvider。
- 判断 权限是否符合
- 创建ContentProvider的记录 ContentProviderRecond
- 判断 启动 ContentResolver的进程 和 ContentProvider 进程是否是同一个 如果是的话直接返回
- ContentProviderRecond 是否在mLaunchingProviders 已开启广播 的集合内,如果不在那么怎进行开启
- 开启ContentProvider 分两种情况,ContentProvider广播所属的进程开启了 但是 ContentProvider 没有启动过 ,那么 调用 proc.thread.scheduleInstallProvider(cpi); 启动 。 ContentProvider广播所属的进程 没开起那么必然 ContentProvider也没开启,那么就需要先开启进程
- 判断 cpr.provider是否为空 如果为空 则wait 等待。cpr.provider 是调用的ContentProvider 的关键 只有在 广播开启成功后才会为其赋值
- 返回 ContentProviderHolder
8. ContentProvider 开启 proc.thread.scheduleInstallProvider(cpi);
目的是 调用ActivityThread的 handleInstallProvider
@Override
public void scheduleInstallProvider(ProviderInfo provider) {
sendMessage(H.INSTALL_PROVIDER, provider);
}
case INSTALL_PROVIDER:
handleInstallProvider((ProviderInfo) msg.obj);
public void handleInstallProvider(ProviderInfo info) {
final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
try {
installContentProviders(mInitialApplication, Lists.newArrayList(info));
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
}
9. ActivityThread.installContentProviders
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<IActivityManager.ContentProviderHolder> results =
new ArrayList<IActivityManager.ContentProviderHolder>();
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());
}
IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
ActivityManagerNative.getDefault().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
}
}
10.ActivityThread.installProvider 开启ContentProviders
当 ContentProvider 没有创建开启时
- 创建ContentProvider
- 利用 ContentProvider.getIContentProvider 获取 IContentProvider
- 调用 ContentProvider 的attach方法
private IActivityManager.ContentProviderHolder installProvider(Context context,
IActivityManager.ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
。。。。。
if (DEBUG_PROVIDER || noisy) {
Slog.d(TAG, "Loading provider " + info.authority + ": "
+ info.name);
}
Context c = null;
ApplicationInfo ai = info.applicationInfo;
if (context.getPackageName().equals(ai.packageName)) {
c = context;
} else if (mInitialApplication != null &&
mInitialApplication.getPackageName().equals(ai.packageName)) {
c = mInitialApplication;
} else {
try {
c = context.createPackageContext(ai.packageName,
Context.CONTEXT_INCLUDE_CODE);
} catch (PackageManager.NameNotFoundException e) {
// Ignore
}
}
if (c == null) {
Slog.w(TAG, "Unable to get context for package " +
ai.packageName +
" while loading content provider " +
info.name);
return null;
}
try {
//创建 BroadCastProvider
final java.lang.ClassLoader cl = c.getClassLoader();
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
// IContentProvider 获取 IContentProvider
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.
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
if (!mInstrumentation.onException(null, e)) {
throw new RuntimeException(
"Unable to get provider " + info.name
+ ": " + e.toString(), e);
}
return null;
}
。。。。。
}
11. ContentProvider.getIContentProvider
这个方法 返回的就是ContentResolve 调用用的 IContentProvider
查看 方法 我们得知返回的是 Transport 类 是 ContentProvider 的内部类 ,继承 ContentProviderNative ,而ContentProviderNative也继承Binder 。也就是说 Transport 即使一个 内部类也是一个 Bind ,这样既符合 能够跨进程调用,并且能够调用 ContentProvider
private Transport mTransport = new Transport();
/**
* Returns the Binder object for this provider.
*
* @return the Binder object for this provider
* @hide
*/
public IContentProvider getIContentProvider() {
return mTransport;
}
12 。返回 到 installContentProviders 方法 在底部有方法
ActivityManagerNative.getDefault().publishContentProviders(
getApplicationThread(), results);
这个是就是为上面 ContentProviderRecord.provider 赋值 ,并 唤醒 上面的wait
public final void publishContentProviders(IApplicationThread caller,
List<ContentProviderHolder> providers) {
if (providers == null) {
return;
}
enforceNotIsolatedCaller("publishContentProviders");
synchronized (this) {
final ProcessRecord r = getRecordForAppLocked(caller);
if (DEBUG_MU)
Slog.v(TAG_MU, "ProcessRecord uid = " + r.uid);
if (r == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when publishing content providers");
}
final long origId = Binder.clearCallingIdentity();
final int N = providers.size();
for (int i=0; i<N; i++) {
ContentProviderHolder src = providers.get(i);
if (src == null || src.info == null || src.provider == null) {
continue;
}
ContentProviderRecord dst = r.pubProviders.get(src.info.name);
if (DEBUG_MU)
Slog.v(TAG_MU, "ContentProviderRecord uid = " + dst.uid);
if (dst != null) {
ComponentName comp = new ComponentName(dst.info.packageName, dst.info.name);
mProviderMap.putProviderByClass(comp, dst);
String names[] = dst.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
mProviderMap.putProviderByName(names[j], dst);
}
int NL = mLaunchingProviders.size();
int j;
for (j=0; j<NL; j++) {
if (mLaunchingProviders.get(j) == dst) {
mLaunchingProviders.remove(j);
j--;
NL--;
}
}
synchronized (dst) {
dst.provider = src.provider;
dst.proc = r;
//唤醒
dst.notifyAll();
}
updateOomAdjLocked(r);
}
}
Binder.restoreCallingIdentity(origId);
}
}