ContentProvider (内容提供者) 属于四大组件之一,可以说它是在四大组件中开发者使用率最少的一个,它的作用就是进程间进行数据交互,底层采用 Binder 机制进行进程间通信。
下面我们就以分析 ContentProvider 工作流程为主来进行全面分析。
源码分析
query 到 AMS 调用过程
下面先来看一个代码示例,代码如下:
fun getContactsLists(): MutableList<String> {
var contactsLists: MutableList<String> = mutableListOf<String>()
lateinit var cursor: Cursor
try {
//使用 getContentResolver() 查询联系人列表
cursor = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null)
//对 cursor 进行遍历
if (cursor != null) {
while (cursor.moveToNext()) {
//电话号码
val number = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
contactsLists.add("电话:$number")
}
}
cursor.close()
} catch (error: Exception) {
logger.error("error:$error")
}
return contactsLists
}
//测试
val contactsLists = getContactsLists()
contactsLists.forEach { it -> println("通过ContentResolver获取联系人: $contactsLists") }
那么这一流程内部是怎么运行的,下面进入代码分析。老规矩,还是看一下该小节分析流程,这里以时序图为主
通过上面代码示例想要 query 数据先要拿到 contentResolver
对象,通过父类 getContentResolver()
方法获得,代码如下:
@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}
这里的 mBase 是 Context 对象,它的实现类就是 ContextImpl
我们直接看它具体实现,代码如下:
//ContextImpl.java
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
这里的 getContentResolver
方法中返回了 ApplicationContentResolver
对象,它是 ContextImpl 的静态内部类,继承自 ContentResolver
,它在 ContextImpl
的构造方法中被创建,就会启动 ContentProvider,这里以上面我们示例 query
来进行分析,我们看它的具体实现,
//ContentResolver.java
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
@Nullable String[] projection, @Nullable Bundle queryArgs,
@Nullable CancellationSignal cancellationSignal) {
Preconditions.checkNotNull(uri, "uri");
1. 拿到 IContentProvider 对象,它是 ContentProvider 的本地代理
IContentProvider unstableProvider = acquireUnstableProvider(uri);
if (unstableProvider == null) {
return null;
}
IContentProvider stableProvider = null;
Cursor qCursor = null;
try {
try {
2. 调用 IContentProvider 的 query 函数来进行 query
qCursor = unstableProvider.query(mPackageName, uri, projection,
queryArgs, remoteCancellationSignal);
} catch (DeadObjectException e) {
//...
return wrapper;
} catch (RemoteException e) {
return null;
} finally {
...
}
}
通过 acquireUnstableProvider
方法拿到 ContentProvider 的本地代理对象,我们先来看下注释 1 的 acquireUnstableProvider
方法怎么拿到 ContentProvider 本地代理对象,代码如下:
//ContentResolver.java
public final IContentProvider acquireUnstableProvider(Uri uri) {
if (!SCHEME_CONTENT.equals(uri.getScheme())) {
return null;
}
String auth = uri.getAuthority();
if (auth != null) {
//调用内部抽象方法
return acquireUnstableProvider(mContext, uri.getAuthority());
}
return null;
}
/** @hide */
protected abstract IContentProvider acquireUnstableProvider(Context c, String name);
//ContextImpl.java
@Override
protected IContentProvider acquireUnstableProvider(Context c, String auth) {
return mMainThread.acquireProvider(c,
ContentProvider.getAuthorityWithoutUserId(auth),
resolveUserIdFromAuthority(auth), false);
}
return 返回的对象,首先拿到 mMainThread
对象,然后调用它内部的 acquireProvider 方法
//ActivityThread.java
public final IContentProvider acquireProvider(
Context c, String auth, int userId, boolean stable) {
1. acquireExistingProvider 方法主要检查 ActivityThread 全局变量 mProviderMap 中是否有目标 ContentProvider 存在,有就返回,没有就通过注释 2 处获取,
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
if (provider != null) {
return provider;
}
ContentProviderHolder holder = null;
try {
2. 调用 IAcitivityManager 获取 ContentProviderHolder 对象
holder = ActivityManager.getService().getContentProvider(
getApplicationThread(), auth, userId, stable);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
...
3. 用来安装 ContentProvider
holder = installProvider(c, holder, holder.info,
true /*noisy*/, holder.noReleaseNeeded, stable);
return holder.provider;
}
总结
1 处的 acquireExistingProvider
方法内部会检查 ActivityThread 的全局变量 mProviderMap 中是否有 ContentProvider 存在
2 处 的 IActivityManager 的 getContentProvider
方法与 AMS 进行通信来获取
3 是安装 ContentProvider,并将 ContentProvider 相关数据存储在 mProviderMap
中,起到缓存作用
我们现在来看下AMS 的 getContentProvider
方法具体实现,代码如下:
//AMS.java
@Override
public final ContentProviderHolder getContentProvider(
IApplicationThread caller, String name, int userId, boolean stable) {
enforceNotIsolatedCaller("getContentProvider");
...
//调动内部 getContentProviderImpl 方法
return getContentProviderImpl(caller, name, null, stable, userId);
}
//AMS.java
private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
String name, IBinder token, boolean stable, int userId) {
ContentProviderRecord cpr;
ContentProviderConnection conn = null;
ProviderInfo cpi = null;
..1. 获取目标 ContentProvider 应用程序进程的信息,如果进程已经启动就调用注释 2 ,否则调用注释 3
ProcessRecord proc = getProcessRecordLocked(
cpi.processName, cpr.appInfo.uid, false);
if (proc != null && proc.thread != null && !proc.killed) {
if (DEBUG_PROVIDER) Slog.d(TAG_PROVIDER,
"Installing in existing process " + proc);
if (!proc.pubProviders.containsKey(cpi.name)) {
checkTime(startTime, "getContentProviderImpl: scheduling install");
proc.pubProviders.put(cpi.name, cpr);
try {
2. 调用 IApplicationThreadscheduleInstallProvider 函数
proc.thread.scheduleInstallProvider(cpi);
} catch (RemoteException e) {
}
}
} else {
checkTime(startTime, "getContentProviderImpl: before start process");
3. 启动新进程
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);
}
}
....
}
2 位置执行流程,首先调用 ActivityThread 的内部类 IApplication 的scheduleInstallProvider
函数,然后通过 H sendMessage 通知进行安装 ContentProvider
看3位置处我们直接看 ActivityThread main 函数,代码如下
//AMS.java
final ProcessRecord startProcessLocked(String processName,
ApplicationInfo info, boolean knownToBeDead, int intentFlags,
String hostingType, ComponentName hostingName, boolean allowWhileBooting,
boolean isolated, boolean keepIfLarge) {
return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,
hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,
null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,
null /* crashHandler */);
}
我们直接看 ActivityThread main 函数,代码如下
//ActivityThread.java
//通过反射调用执行的
public static void main(String[] args) {
....
//主线程消息循环
Looper.prepareMainLooper();
//创建 ActivityThread 对象
ActivityThread thread = new ActivityThread();
//Application,Activity 入口
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
主要看 ActivityThread attach 函数具体实现,代码如下:
//ActivityThread.java
private void attach(boolean system) {
...
final IActivityManager mgr = ActivityManager.getService();
try {
关联 Application
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
...
}
从上得到 AMS 的代理类 IActivityManager ,在注释 调用 AMS 的 attachApplication
函数,并将 IApplicationThread 对象传入 AMS 保持应用进程和 AMS 跨进程通信,应用程序调用 AMS 的过程就分析完了,下面我们分析 AMS 到应用程序进程的 ContentProvider 安装过程
AMS 启动 ContentProvider 的过程
先看流程图
//AMS.java
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
...
thread.bindApplication(processName, appInfo, providers,
app.instr.mClass,
profilerInfo, app.instr.mArguments,
app.instr.mWatcher,
app.instr.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial);
...
}
在 attachApplicationLocked
函数中调用了 thread.bindApplication 方法,thread 是 IApplicationThread ,这里和 IActivityManager 一样采用了 aidl 进行进程间传输数据,我们回到 ActivityThread 内部类 ApplicationThread 的 bindApplication
方法
//ActivityThread.java
public final void bindApplication(String processName, ApplicationInfo appInfo,
List<ProviderInfo> providers, ComponentName instrumentationName,
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
String buildSerial) {
if (services != null) {
// Setup the service cache in the ServiceManager
ServiceManager.initServiceCache(services);
}
setCoreSettings(coreSettings);
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providers;
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableBinderTracking = enableBinderTracking;
data.trackAllocation = trackAllocation;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
data.buildSerial = buildSerial;
//发送消息给 H 类
sendMessage(H.BIND_APPLICATION, data);
}
ActivityThread 内部类 H 收到消息,开始处理 BIND_APPLICSTION
消息,代码如下:
//ActivityThread.java
...
public void handleMessage(Message msg) {
...
case BIND_APPLICATION:
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
break;
...
}
...
//ActivityThread.java
...
public void handleMessage(Message msg) {
...
case BIND_APPLICATION:
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
break;
...
}
...
//ActivityThread.java
private void handleBindApplication(AppBindData data) {
...
1. 创建 ContentImpl
final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
...
2 通过反射创建 Instrumentation
final ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.getClassName()).newInstance();
} catch (Exception e) {
...
}
final ComponentName component = new ComponentName(ii.packageName, ii.name);
3 处初始化 Instrumentaion
mInstrumentation.init(this, instrContext, appContext, component,
data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
....
try {
创建 Application
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
5 ContentProvider 启动的
installContentProviders(app, data.providers);
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
}
try {
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
...
}
try {
6 调用 Application onCreate 生命周期方法
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
...
}
}
注释 5 看 ContentProvider 是如何启动的,代码如下:
//ActivityThread.java
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
1 遍历当前应用程序进程的 ProviderInfo 列表,得到每个 ContentProvicer 的存储信息
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 调用 installProvider 方法来启动这些 ContentProvider
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
3 处是将启动了的 ContentProvider 存入 AMS 的 mProviderMap 中 就是用来缓存启动过的 ContentProvidec
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
现在来看调用 installProvider 方法来启动这些 ContentProvider
//ActivityThread.java
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
....
try {
final java.lang.ClassLoader cl = c.getClassLoader();
1 通过反射实例化 ContentProvider 对象
localProvider = (ContentProvider)cl.
loadClass(info.name).newInstance();
provider = localProvider.getIContentProvider();
...
2处调用它的 attachInfo 方法
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;
}
....
}
在注释 2 处调用它的 attachInfo
方法,代码如下
//ContentProvider.java
public void attachInfo(Context context, ProviderInfo info) {
attachInfo(context, info, false);
}
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
mNoPerms = testing;
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);
}
/**
* 安装成功,调用生命周期函数 onCreate
*/
ContentProvider.this.onCreate();
}
}
public abstract boolean onCreate();
可以看到最后在 ContentProvider 的 attachInfo
函数中进行调用了抽象方法 onCreate, 那么它的子类就会进行实现 onCreate 达到启动成功的通知。