腾讯微视Android高级岗:说说ContentProvider的启动过程(1)

既然ContentProvider这么重要,我们有必要从源码的角度深挖一下ContentProvider的启动过程。

二、源码分析

1、ContentProvider的创建与初始化

在具体分析源码之前,我们大致看一下ContentProvider的整个启动过程的示意图,如下所示:

App启动后,会调用ActivityThread的main方法,接着在main方法中创建了ActivityThread对象,并调用了它的attach方法。在attach方法中远程调用AMS的attachApplication方法,该方法中又远程调用PMS的queryContentProviders方法获取应用注册的Provider信息,然后调用ApplicationThread的bindApplication方法将Provider信息传递过去。在ApplicationThread中通过makeApplication生成Application对象,并调用installContentProviders方法初始化并加载ContentProvider,然后调用Application对象的onCreate方法,应用就这样跑起来了。

下面我们具体分析上述的每一个步骤。

首先是App启动,调用了ActivityThread的main方法,如下所示:

public static void main(String[] args) {
…省略

// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);

Process.setArgV0(“”);

Looper.prepareMainLooper();

ActivityThread thread = new ActivityThread();
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();
}

main方法中,创建了ActivityThread对象,然后调用了该对象的attach方法,如下所示:

private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
ViewRootImpl.addFirstDrawHandler(new Runnable() {
@Override
public void run() {
ensureJitEnabled();
}
});
android.ddm.DdmHandleAppName.setAppName(“”,
UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
…省略
} else {
…省略
}
…省略
}

系统的应用我们不作分析,而对于非系统应用,ActivityManager.getService()获取到一个IActivityManager对象,它是一个Binder对象,通过它调用attachApplication方法实际上就是远程调用了ActivityManagerServiceattachApplication方法,如下所示:

public final void attachApplication(IApplicationThread thread) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid);
Binder.restoreCallingIdentity(origId);
}
}

attachApplication方法又调用了attachApplicationLocked方法,如下所示:

private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {

…省略

EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.userId, app.pid, app.processName);

app.makeActive(thread, mProcessStats);
app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
app.curSchedGroup = app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
app.forcingToImportant = null;
updateProcessForegroundLocked(app, false, false);
app.hasShownUi = false;
app.debugging = false;
app.cached = false;
app.killedByAm = false;
app.killed = false;

app.unlocked = StorageManager.isUserKeyUnlocked(app.userId);

mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);

boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
//获取应用中注册的ContentProvider数据
List providers = normalMode ? generateApplicationProvidersLocked(app) : null;

if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
msg.obj = app;
mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
}

checkTime(startTime, “attachApplicationLocked: before bindApplication”);
try {
…省略

// If we were asked to attach an agent on startup, do so now, before we’re binding
// application code.
if (agent != null) {
thread.attachAgent(agent);
}

checkTime(startTime, “attachApplicationLocked: immediately before bindApplication”);
mStackSupervisor.mActivityMetricsLogger.notifyBindApplication(app);
if (app.instr != null) {
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);
} else {
thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial);
}

checkTime(startTime, “attachApplicationLocked: immediately after bindApplication”);
updateLruProcessLocked(app, false, null);
checkTime(startTime, “attachApplicationLocked: after updateLruProcessLocked”);
app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
} catch (Exception e) {
…省略
}

…省略

return true;
}

在attachApplicationLocked方法中,主要流程分为两步:

  • 在正常模式下,调用了generateApplicationProvidersLocked方法获取注册的ContentProvider信息。
  • 将上一步中的ContentProvider信息作为参数,远程调用了ApplicationThread的bindApplication方法。

我们首先看第一步,generateApplicationProvidersLocked方法如下所示:

private final List generateApplicationProvidersLocked(ProcessRecord app) {
List providers = null;
try {
//远程调用PMS获取应用中注册的ContentProvider信息
providers = AppGlobals.getPackageManager()
.queryContentProviders(app.processName, app.uid,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS
| MATCH_DEBUG_TRIAGED_MISSING, /metadastaKey=/ null)
.getList();
} catch (RemoteException ex) {
}
int userId = app.userId;
…省略
return providers;
}

在上述方法中,AppGlobals.getPackageManager()方法返回的是IPackageManager,调用了它的queryContentProviders方法。这是一个Binder对象,其实际调用的是PackageManagerService的queryContentProviders方法,如下所示:

public @NonNull ParceledListSlice queryContentProviders(String processName,
int uid, int flags, String metaDataKey) {
final int callingUid = Binder.getCallingUid();
final int userId = processName != null ? UserHandle.getUserId(uid)
: UserHandle.getCallingUserId();
if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
flags = updateFlagsForComponent(flags, userId, processName);
ArrayList finalList = null;
// reader
synchronized (mPackages) {
final Iterator<PackageParser.Provider> i = mProviders.mProviders.values().iterator();
while (i.hasNext()) {
final PackageParser.Provider p = i.next();
PackageSetting ps = mSettings.mPackages.get(p.owner.packageName);
if (ps != null && p.info.authority != null
&& (processName == null
|| (p.info.processName.equals(processName)
&& UserHandle.isSameApp(p.info.applicationInfo.uid, uid)))
&& mSettings.isEnabledAndMatchLPr(p.info, flags, userId)) {

// See PM.queryContentProviders()'s javadoc for why we have the metaData
// parameter.
if (metaDataKey != null
&& (p.metaData == null || !p.metaData.containsKey(metaDataKey))) {
continue;
}
final ComponentName component =
new ComponentName(p.info.packageName, p.info.name);
if (filterAppAccessLPr(ps, callingUid, component, TYPE_PROVIDER, userId)) {
continue;
}
if (finalList == null) {
finalList = new ArrayList(3);
}
ProviderInfo info = PackageParser.generateProviderInfo(p, flags,
ps.readUserState(userId), userId);
if (info != null) {
finalList.add(info);
}
}
}
}

if (finalList != null) {
Collections.sort(finalList, mProviderInitOrderSorter);
return new ParceledListSlice(finalList);
}

return ParceledListSlice.emptyList();
}

queryContentProviders方法很长,但是其主要做的事情是,遍历了mProviders.mProviders中的值,而mProviders.mProviders的一个ArrayMap,value为PackageParse.Provider对象。所以,我们需要知道mProviders.mProviders是在哪里被赋值的,最后找到了PackageManagerService.ProviderIntentResolver的addProvider方法,如下所示:

public final void addProvider(PackageParser.Provider p) {
if (mProviders.containsKey(p.getComponentName())) {
Slog.w(TAG, “Provider " + p.getComponentName() + " already defined; ignoring”);
return;
}

mProviders.put(p.getComponentName(), p);
if (DEBUG_SHOW_INFO) {
Log.v(TAG, " "

  • (p.info.nonLocalizedLabel != null
    ? p.info.nonLocalizedLabel : p.info.name) + “:”);
    Log.v(TAG, " Class=" + p.info.name);
    }
    final int NI = p.intents.size();
    int j;
    for (j = 0; j < NI; j++) {
    PackageParser.ProviderIntentInfo intent = p.intents.get(j);
    if (DEBUG_SHOW_INFO) {
    Log.v(TAG, " IntentFilter:");
    intent.dump(new LogPrinter(Log.VERBOSE, TAG), " ");
    }
    if (!intent.debugCheck()) {
    Log.w(TAG, "==> For Provider " + p.info.name);
    }
    addFilter(intent);
    }

该方法中,将参数中的PackageParser.Provider对象put了进来,而PackageParser.Provider对象存储的就是每一个ContentProvider的信息。在PackageManagerService的commitPackageSettings方法中调用了上述方法,commitPackageSettings方法如下所示:

private void commitPackageSettings(PackageParser.Package pkg, PackageSetting pkgSetting,
UserHandle user, int scanFlags, boolean chatty) throws PackageManagerException {
…省略
//存储ContentProvider的信息
int N = pkg.providers.size();
StringBuilder r = null;
int i;
for (i=0; i<N; i++) {
PackageParser.Provider p = pkg.providers.get(i);
p.info.processName = fixProcessName(pkg.applicationInfo.processName,
p.info.processName);
mProviders.addProvider§;
p.syncable = p.info.isSyncable;
if (p.info.authority != null) {
String names[] = p.info.authority.split(“;”);
p.info.authority = null;
for (int j = 0; j < names.length; j++) {
if (j == 1 && p.syncable) {
// We only want the first authority for a provider to possibly be
// syncable, so if we already added this provider using a different
// authority clear the syncable flag. We copy the provider before
// changing it because the mProviders object contains a reference
// to a provider that we don’t want to change.
// Only do this for the second authority since the resulting provider
// object can be the same for all future authorities for this provider.
p = new PackageParser.Provider§;
p.syncable = false;
}
if (!mProvidersByAuthority.containsKey(names[j])) {
mProvidersByAuthority.put(names[j], p);
if (p.info.authority == null) {
p.info.authority = names[j];
} else {
p.info.authority = p.info.authority + “;” + names[j];
}
if (DEBUG_PACKAGE_SCANNING) {
if (chatty)
Log.d(TAG, "Registered content provider: " + names[j]

  • ", className = " + p.info.name + ", isSyncable = "
  • p.info.isSyncable);
    }
    } else {
    PackageParser.Provider other = mProvidersByAuthority.get(names[j]);
    Slog.w(TAG, "Skipping provider name " + names[j] +
    " (in package " + pkg.applicationInfo.packageName +
    "): name already used by "
  • ((other != null && other.getComponentName() != null)
    ? other.getComponentName().getPackageName() : “?”));
    }
    }
    }
    if (chatty) {
    if (r == null) {
    r = new StringBuilder(256);
    } else {
    r.append(’ ');
    }
    r.append(p.info.name);
    }
    }
    if (r != null) {
    if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Providers: " + r);
    }

//存储Service的信息
N = pkg.services.size();
r = null;
for (i=0; i<N; i++) {
PackageParser.Service s = pkg.services.get(i);
s.info.processName = fixProcessName(pkg.applicationInfo.processName,
s.info.processName);
mServices.addService(s);
if (chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(’ ');
}
r.append(s.info.name);
}
}
if (r != null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Services: " + r);
}

//存储BroadcastReceiver的信息
N = pkg.receivers.size();
r = null;
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.receivers.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName);
mReceivers.addActivity(a, “receiver”);
if (chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(’ ');
}
r.append(a.info.name);
}
}
if (r != null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Receivers: " + r);
}

//存储Activity的信息
N = pkg.activities.size();
r = null;
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.activities.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName);
mActivities.addActivity(a, “activity”);
if (chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(’ ');
}
r.append(a.info.name);
}
}
if (r != null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Activities: " + r);
}

…省略
}

可以看到,commitPackageSettings方法中不仅仅存储了ContentProvider的信息,还存储了Service、BroadcastReceiver、Activity等信息,而PackageParser.Provider对象是遍历PackageParser.Package的providers获取的,而PackageParser.Package的providers就是通过解析AndroidManifest.xml得到的。PackageParser的parseBaseApplication方法就是解析方法,如下所示:

private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
…省略

while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}

String tagName = parser.getName();
if (tagName.equals(“activity”)) {
Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false,
owner.baseHardwareAccelerated);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}

owner.activities.add(a);

} else if (tagName.equals(“receiver”)) {
Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs,
true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}

owner.receivers.add(a);

} else if (tagName.equals(“service”)) {
Service s = parseService(owner, res, parser, flags, outError, cachedArgs);
if (s == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}

owner.services.add(s);

} else if (tagName.equals(“provider”)) {
Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs);
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}

owner.providers.add§;

} else {
if (!RIGID_PARSER) {
Slog.w(TAG, "Unknown element under : " + tagName

  • " at " + mArchiveSourcePath + " "
  • parser.getPositionDescription());
    XmlUtils.skipCurrentTag(parser);
    continue;
    } else {
    outError[0] = "Bad element under : " + tagName;
    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
    return false;
    }
    }
    }
    …省略

return true;
}

可以看到对于AndroidManifest中的tagName为provider,则解析其中的信息并生成PackageParser.Provider对象。

通过PMS获取ContentProvider列表信息的流程到此结束,通过上述分析,我们也验证了自定义的ContentProvider必须在AndroidManifest中注册这样的一个结论。

我们继续回到AMS的attachApplicationLocked方法中,在拿到了应用注册的ContentProvider信息后,远程调用了ApplicationThread的bindApplication方法,如下所示:

public final void bindApplication(String processName, ApplicationInfo appInfo,
List 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;
sendMessage(H.BIND_APPLICATION, data);
}

注意:参数中的上述的providers就是从PMS中获取的ContentProvider信息。

该方法发送了一个消息,消息ID是H.BIND_APPLICATION。在mH对象的handleMessage方法中处理该消息,并调用handleBindApplication方法,如下所示:

private void handleBindApplication(AppBindData data) {
…省略

Application app;
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();
try {
app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;

if (!data.restrictedBackupMode) {
//ContentProvider列表不为空
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers); //初始化并加载ContentProvider
// For process that contains content providers, we want to
// ensure that the JIT is enabled “at some point”.
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);
    }
    //调用Application的onCreate方法
    try {
    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 {
    // If the app targets < O-MR1, or doesn’t change the thread policy
    // during startup, clobber the policy to maintain behavior of b/36951662
    if (data.appInfo.targetSdkVersion <= Build.VERSION_CODES.O
    || StrictMode.getThreadPolicy().equals(writesAllowedPolicy)) {
    StrictMode.setThreadPolicy(savedPolicy);
    }
    }

…省略
}

在handleBindApplication方法中,判断如果ContentProvider列表不为空,则调用installContentProviders方法,如下所示:

private void installContentProviders(
Context context, List providers) {
final ArrayList results = new ArrayList<>();

for (ProviderInfo cpi : providers) {
ContentProviderHolder cph = installProvider(context, null, cpi,
false /noisy/, true /noReleaseNeeded/, true /stable/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}

try {
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}

对于传递过来的ContentProvider列表,遍历该列表,取出每一个ProviderInfo对象,并调用installProvider方法(第二个参数传过来的是null)。如下所示:

private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;

总结

其实要轻松掌握很简单,要点就两个:

  1. 找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。
  2. 多练。 (视频优势是互动感强,容易集中注意力)

你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。下面资料部分截图是我花费几个月时间整理的,诚意满满:特别适合有3-5年开发经验的Android程序员们学习。


《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
ll**)。如下所示:

private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;

总结

其实要轻松掌握很简单,要点就两个:

  1. 找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。
  2. 多练。 (视频优势是互动感强,容易集中注意力)

你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。下面资料部分截图是我花费几个月时间整理的,诚意满满:特别适合有3-5年开发经验的Android程序员们学习。

[外链图片转存中…(img-CdIdUYoC-1715361889984)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 29
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值