1.Activity的启动流程
当我们点击桌面的图标时,其实就是启动对应应用的入口Activity。启动Activity主要有两个流程,一个是对应的应用进程存在,一个是对应的进程不存在,以下的流程是对应的进程不存在的流程。
如上图所示,当启动一个activity时,会将请求发送至AMS,AMS会判断目标进程是否存在,若不存在,则先发消息给zygote进程,然后从zygote进程当中fork出目标进程,接着目标进程会发消息给AMS,告诉AMS目标进程已经启动,然后AMS会调用bindApplication,告诉应用端创建Application,然后调用scheduleTransaction,启动对应的Activity。
下图为详细的调用时序图
有几个地方我们可以重点关注一下
1.startSpecificActivityLocked方法,判断当前进程是否存在,若存在,则直接启动对应的activity,若不存在,则启动进程
void startSpecificActivityLocked(ActivityRecord r,
boolean andResume, boolean checkConfig) {
......
if (app != null && app.thread != null) {
try {
......
//1.若进程存在,则直接启动activity
realStartActivityLocked(r, app, andResume, checkConfig);
return;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting activity "
+ r.intent.getComponent().flattenToShortString(), e);
}
}
//2.启动进程
mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
"activity", r.intent.getComponent(), false, false, true);
}
2.startProcessLocked,AMS启动进程如注释1所示,在启动进程的同时,会发送一个进程timeout的延迟消息,延迟的时间根据参数为12秒或者10秒,若在这段时间内进程未启动,则会触发这个消息执行,将会清除改进程相关内容。这里我们可以看出,进程启动的这个timeout机制和ANR的原理是一模一样的。
private boolean startProcessLocked(String hostingType, String hostingNameStr, String entryPoint,
ProcessRecord app, int uid, int[] gids, int runtimeFlags, int mountExternal,
String seInfo, String requiredAbi, String instructionSet, String invokeWith,
long startTime) {
......
try {
//1.启动进程
final ProcessStartResult startResult = startProcess(hostingType, entryPoint, app,
uid, gids, runtimeFlags, mountExternal, seInfo, requiredAbi, instructionSet,
invokeWith, startTime);
//2.进程启动timeout消息
handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
startSeq, false);
} catch (RuntimeException e) {
......
}
......
}
3.attachApplicationLocked,应用进程启动之后,会立马调用attachApplication,然后最终会走到attachApplicationLocked方法,这里就将进程启动阶段设置的延迟消息remove掉了。
此时应用进程已经启动了,接着就可以处理activity了,如注释3所示。
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid, int callingUid, long startSeq) {
......
//1.remove timeout消息
mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
......
//2.让应用进程初始化
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, isAutofillCompatEnabled);
//3.启动activity
mStackSupervisor.attachApplicationLocked(app);
//4.处理service
mServices.attachApplicationLocked(app, processName);
//5.处理broadcast
sendPendingBroadcastsLocked(app);
......
}
4.realStartActivityLocked,如注释1所示,创建activity的启动transaction,然后将其发送到应用端。
final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
boolean andResume, boolean checkConfig) throws RemoteException {
......
//1. 创建activity launch transaction.
final ClientTransaction clientTransaction = ClientTransaction.obtain(app.thread,
r.appToken);
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, app.repProcState, r.icicle,
r.persistentState, results, newIntents, mService.isNextTransitionForward(),
profilerInfo));
// Set desired final state.
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(mService.isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
clientTransaction.setLifecycleStateRequest(lifecycleItem);
// 2.Schedule transaction
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
......
}
5.应用端收到请求会调用ActivityThread的handleLaunchActivity,然后调用performLaunchActivity,此方法会创建Activity,并调用对应的周期函数。
2.总结
1.冷启动优化
冷启动的时间我们可以大致分成两个阶段,
第一阶段:AMS调度+zygote进程启动+进程初始化
第二阶段:Application初始化+Activity的初始化和现实+自身业务的任务
通常我们测试冷启动的时间是从手指离开应用图标开始到应用进程完全显示且图像静止,而这个时间包含上述两段时间,第一个阶段是应用层很难去影响的,对于单个应用来说,尽可能的去优化第二个阶段。
2.自身应用所占用的内存
当我们在测试自身应用所占内存时,有时候会发现,明明没有做什么占用内存的修改,怎么这个版本比上个版本所占内存增加了这么多呢?这时我们可以通过相关工具对比一下是否是zygote进程占用内存增加了,因为所有的应用进程均从zygote进程继承而来,所以若zygote进程占用内存增加,那么应用的内存也会相应地增加。