通过 startService 在新进程中启动服务的流程(二)
在《通过 startService 在新进程中启动服务的流程(一)》中我们分析到了用 Process.start 启动了一个新的 ActivityThread,本文中接着继续分析 startService 剩下的流程。
5. ActivityThread 入口
前一篇说道,作为一个 java 入口类,ActivityThread 有自己的 main 函数,总的来看该函数做了两件事:1. ActivityThread.attach,2. Looper.loop(这也就是每个 android 应用程序的进程中主线程的 Looper 了)。除此之外,有几个有意思的点可以提一下:
public static void main(String[] args) {
1. SamplingProfilerIntegration.start();
......
2. Process.setArgV0("<pre-initialized>");
......
}
- SamplingProfilerIntegration.start() 在DDMS中打印出的内存快照hprof文件实际上与此相关,如果感兴趣的话,可以查阅 dalvik.system.profiler.SamplingProfiler 这个类的相关说明。
- 我们发现在主进程Looper开始循环、ActivityThread.attach 执行之前,进程名叫做"<pre-initialized>"。
在这里只对 ActivityThread.attach 的过程进行展开分析。
5.1 ActivityThread.attach
本文讨论的启动流程为非系统进程,对于非系统进程,先把进程所属的 ApplicationThread 设置到 RunntimeInit 中(用于 report 错误),然后再将其传递给 AMS,执行 attachApplication:
private void attach(boolean system) {
sThreadLocal.set(this);
mSystemThread = system;
if (!system) {
ViewRootImpl.addFirstDrawHandler(new Runnable() {
public void run() {
ensureJitEnabled();
}
});
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>");
RuntimeInit.setApplicationObject(mAppThread.asBinder());
IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
// Ignore
}
} else {
......
}
......
}
5.2 ActivityManagerService.attachApplicationLocked
AMS 中 attachApplication 又调到了 attachApplicationLocked, 第一步是从 mPidsSelfLocked 中取该进程对应的 ProcessRecord,这个 ProcessRecord 也就是《通过 startService 在新进程中启动服务的流程(一)》中讲的在 startProcessLocked 过程的最后中放入的(Process.start 之后,超时逻辑之前)。
如果是没有找到目标进程对应的 ProcessRecord,则判为非法进程,并kill掉:
private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
// Find the application record that is being attached... either via
// the pid if we are running in multiple processes, or just pull the
// next app record if we are emulating process with anonymous threads.
ProcessRecord app;
if (pid != MY_PID && pid >= 0) {
synchronized (mPidsSelfLocked) {
app = mPidsSelfLocked.get(pid);
}
} else {
app = null;
}
if (app == null) {
Slog.w(TAG, "No pending application record for pid " + pid
+ " (IApplicationThread " + thread + "); dropping process");
EventLog.writeEvent(EventLogTags.AM_DROP_PROCESS, pid);
if (pid > 0 && pid != MY_PID) {
Process.killProcessQuiet(pid);
} else {
try {
thread.scheduleExit();
} catch (Exception e) {
// Ignore exceptions.
}
}
return false;
}
......
}
若成功查找到了指定 ProcessRecord,将该 ApplicationThread 的 IBinder 对象绑定给 ProcessRecord,并取消超时处理(在 startProcessLocked 最后会启动超时逻辑):
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
......
EventLog.writeEvent(EventLogTags.AM_PROC_BOUND, app.pid, app.processName);
app.thread = thread;
app.curAdj = app.setAdj = -100;
app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
app.setSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
app.forcingToForeground = null;
app.foregroundServices = false;
app.hasShownUi = false;
app.debugging = false;
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
......
}
将在 AMS 中查到的 processName(进程名)、ApplicationInfo、Providers 等信息传至目标进程中执行 bindApplication,其中 Providers 即查找 APP 下的所有声明的可用的 Provider 组件:
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
......
boolean normalMode = mProcessesReady || isAllowedWhileBooting(app.info);
List providers = normalMode ? generateApplicationProvidersLocked(app) : null;
......
try {
......
thread.bindApplication(processName, appInfo, providers,
app.instrumentationClass, profileFile, profileFd, profileAutoStop,
app.instrumentationArguments, app.instrumentationWatcher, testMode,
isRestrictedBackupMode || !normalMode, app.persistent,
mConfiguration, app.compat, getCommonServicesLocked(),
mCoreSettingsObserver.getCoreSettingsLocked());
}
......
}
5.3 ApplicationThread.bindApplication
ApplicationThread 的 bindApplication 方法发消息至主线程(H 是个神奇的名字,当然它是个主线程的 Handler)执行 handleBindApplication:
public final void bindApplication(...) {
......
queueOrSendMessage(H.BIND_APPLICATION, data);
}
在 handleBindApplication 中我们去掉不关心的流程,大体分为6个步骤:
<center>
图 5.1 handleBindApplication 流程简介
</center>
代码如下所示:
private void handleBindApplication(AppBindData data) {
......
1. Process.setArgV0(data.processName);
android.ddm.DdmHandleAppName.setAppName(data.processName);
......
/*
* Before spawning a new process, reset the time zone to be the system time zone.
* This needs to be done because the system time zone could have changed after the
* the spawning of this process. Without doing this this process would have the incorrect
* system time zone.
*/
2. TimeZone.setDefault(null);
/*
* Initialize the default locale in this process for the reasons we set the time zone.
*/
Locale.setDefault(data.config.locale);
......
3. if (data.debugMode != IApplicationThread.DEBUG_OFF) {
// XXX should have option to change the port.
Debug.changeDebugPort(8100);
if (data.debugMode == IApplicationThread.DEBUG_WAIT) {
Slog.w(TAG, "Application " + data.info.getPackageName()
+ " is waiting for the debugger on port 8100...");
IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.showWaitingForDebugger(mAppThread, true);
} catch (RemoteException ex) {
}
Debug.waitForDebugger();
try {
mgr.showWaitingForDebugger(mAppThread, false);
} catch (RemoteException ex) {
}
} else {
Slog.w(TAG, "Application " + data.info.getPackageName()
+ " can be debugged on port 8100...");
}
}
/**
* Initialize the default http proxy in this process for the reasons we set the time zone.
*/
4. IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
try {
ProxyProperties proxyProperties = service.getProxy();
Proxy.setHttpProxySystemProperty(proxyProperties);
} catch (RemoteException e) {}
......
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
// don't bring up providers in restricted mode; they may depend on the
// app's custom Application class
5. if (!data.restrictedBackupMode){
List<ProviderInfo> providers = data.providers;
if (providers != null) {
installContentProviders(app, providers);
// 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 {
6. mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
}
上面代码虽然已经减了不少,但是贴的还是有点啰嗦了。有几个有意思的点:
- 真正的进程名是在此函数中正式赋值的;
- 我们平时开发、调试应用时弹出的“Waiting For Debugger”是在此函数中弹的;
- 在restricted mode中不会将Provider带起来;
- Provider的onCreate执行(对应installContentProviders)在Application的onCreate(对应mInstrumentation.callApplicationOnCreate)之前;
5.4 启动目标服务
上面的是捋的 ApplicationThread.bindApplication 这条线,我们回到 ActivityManagerService.attachApplicationLocked 方法中,继续来看。比较关键的流程就剩下三块,正如代码中注释所示:
- See if the top visible activity is waiting to run in this process...
- Find any services that should be running in this process...
- Check if the next broadcast receiver is in this process...
我们只需关注第二点,将存在 mPendingServices 中的服务启动。从《通过 startService 在新进程中启动服务的流程(一)》中我们知道,在 bringUpServiceLocked 执行的最后将新建的 ServiceRecord 存在 mPendingServices 中,实际上就是要启动的但是进程还没准备好而没启动的服务列表了。
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
......
// Find any services that should be running in this process...
if (!badApp && mPendingServices.size() > 0) {
ServiceRecord sr = null;
try {
for (int i=0; i<mPendingServices.size(); i++) {
sr = mPendingServices.get(i);
if (app.info.uid != sr.appInfo.uid
|| !processName.equals(sr.processName)) {
continue;
}
mPendingServices.remove(i);
i--;
realStartServiceLocked(sr, app);
didSomething = true;
}
} catch (Exception e) {
Slog.w(TAG, "Exception in new application when starting service "
+ sr.shortName, e);
badApp = true;
}
}
......
}
真正启动的函数就是 realStartServiceLocked 了,该函数我们需要关注的点有三个:
- 保存至已经运行的服务列表中(LRU)
- Service.onCreate
- Service.onStartCommand
private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app) throws RemoteException {
......
1. updateLruProcessLocked(app, true, true);
boolean created = false;
try {
......
2. app.thread.scheduleCreateService(r, r.serviceInfo,
compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo));
r.postNotification();
created = true;
}
......
3. sendServiceArgsLocked(r, true);
}
1)updateLruProcessLocked 函数中就是将 ProcessRecord 插入到 mLruProcesses 中,key是传过来的 ApplicationThread 的 binder 对象,从《通过 startService 在新进程中启动服务的流程(一)》中我们知道,在 startServiceLocked 执行时首先通过调用者传进来的 IApplicationThread 查询调用者进程是否存在。
2)scheduleCreateService 实际binder调用到 ActivityThread 中的 scheduleCreateService,然后消息传递到主线程(H)执行 handleCreateService,我们可以看到 attach Context 的过程和 onCreate 的执行:
private void handleCreateService(CreateServiceData data) {
......
try {
ContextImpl context = new ContextImpl();
context.init(packageInfo, null, this);
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app, ActivityManagerNative.getDefault());
service.onCreate();
mServices.put(data.token, service);
......
} catch (Exception e) {
......
}
}
3)sendServiceArgsLocked 实际binder调用到 ActivityThread 中的 scheduleServiceArgs,然后消息传递到主线程执行 handleServiceArgs:
private void handleServiceArgs(ServiceArgsData data) {
Service s = mServices.get(data.token);
if (s != null) {
try {
if (data.args != null) {
data.args.setExtrasClassLoader(s.getClassLoader());
}
int res;
if (!data.taskRemoved) {
res = s.onStartCommand(data.args, data.flags, data.startId);
}
......
} catch (Exception e) {
......
}
}
}
5.5 小结
至此,startService 的流程基本完了,围绕 AMS、PMS、ActivityThread,大体流程可以总结为:1. 搜集目标服务/进程的信息,2. startProcessLocked,3. realStartServiceLocked。