女儿拿着小天才电话手表问我App启动流程

前言

首先,new一个女儿,

  • var mDdaughter = new 女儿("6岁",“漂亮可爱”,“健康乖巧”,“最喜欢玩小天才电话手表和她的爸爸”)

好了,女儿有了,有一天,女儿问我:

“爸爸爸爸,你说我玩的这个小天才电话手表怎么这么厉害,随便点一下这个小图片,这个应用就冒出来了,就可以听儿歌了。好神奇啊。”

我心里一惊:

小天才电话手表的系统就是Android,所以这不就是。。面试官常考的应用启动流程嘛!
女儿也要来面试我了吗!?
好了,既然女儿问了,那就答吧。
但是,对付这个小小的0经验面试官,我该咋说呢?

解答小小面试官

女儿,你可以把手表里面想象成一个幼儿园,里面有一个老师,一个班长,一个班干部,以及一大堆小朋友。

  • 一个老师:Z老师(Zygote进程)
  • 一个班长:小A(ActivityManagerService)
  • 一个班干部:小L(Launcher桌面应用)
  • 一大堆小朋友:所有应用,包括音乐小朋友,聊天小朋友,日历小朋友等等。

应用启动过程就像一个小朋友被叫醒一样,开机之后呢,Z老师会依次叫醒班长和班干部(SystemServer#ActivityManagerService,Launcher),小L醒了之后就会去了解手表里有哪些小朋友,长什么样(icon,name),家庭信息(包名,androidmanifest)等等,然后一个个把小朋友的照片(icon)贴到自己的身上。比如有音乐小朋友,聊天小朋友,日历小朋友,其实也就是你手表上这个桌面啦。

这时候你要点开一个音乐小朋友呢(startActivity),小L就会通知班长小A(Binder),小A知道了之后,让小L自己休息下(Paused),然后就去找Z老师了。Z老师就负责叫音乐小朋友起床了(fork进程,启动ActivityThread),音乐小朋友起来后就又找小A带她去洗脸刷牙(启动ApplicationThread,Activity),都弄完了就可以进行各种表演了,唱歌啊,跳舞啊。

不是很明白啊?我们一起聊个天你就懂了,假如我是Launcher

女儿似懂非懂的给我点了一个赞?,爸爸你真棒。

十五年后

  • mDdaughter.grow(15)
  • mDdaughter.study("Android")

过了十五年,女儿已经21岁了,正在学习Android,考虑要不要女从父业。

这天,她一脸疑惑的来找我:“爸,这个app启动到底是怎么个流程啊,我看了好久还是不大明白,要不你再跟我详细讲一遍吧?” “好嘞,别担心,我这次详细跟你说说”

解答Android程序媛

还记得我小时候跟你说过的故事吗,Android系统就像一个幼儿园,有一个大朋友叫Launcher,身上会贴很多其他小朋友的名片。这个Launcher就是我们的桌面了,它通过PackageManagerService获知了系统里所有应用的信息,并展示了出来,当然它本身也是一个应用。

通过点击一个应用图标,也就是触发了点击事件,最后会执行到startActivity方法。这里也就和启动Activity步骤重合上了。

那么这个startActivity干了啥?是怎么通过重重关卡唤醒这个应用的?

首先,介绍下系统中那些重要的成员,他们在app启动流程中都担任了重要的角色.

系统成员介绍

  • init进程,Android系统启动后,Zygote并不是第一个进程,而是linux的根进程init进程,然后init进程才会启动Zygote进程。
  • Zygote进程,所有android进程的父进程,当然也包括SystemServer进程
  • SystemServer进程,正如名字一样,系统服务进程,负责系统中大大小小的事物,为此也是启动了三员大将(ActivityManagerService,PackageManagerService,WindowManagerService)以及binder线程池。
  • ActivityManagerService,主要负责系统中四大组件的启动、切换、调度及应用进程的管理和调度等工作,对于一些进程的启动,都会通过Binder通信机制传递给AMS,再处理给Zygote。
  • PackageManagerService,主要负责应用包的一些操作,比如安装,卸载,解析AndroidManifest.xml,扫描文件信息等等。
  • WindowManagerService,主要负责窗口相关的一些服务,比如窗口的启动,添加,删除等。
  • Launcher,桌面应用,也是属于应用,也有自己的Activity,一开机就会默认启动,通过设置Intent.CATEGORY_HOME的Category隐式启动。

搞清楚这些成员,就跟随我一起看看怎么过五关斩六将,最终启动了一个App。

第一关:跨进程通信,告诉系统我的需求

首先,要告诉系统,我Launcher要启动一个应用了,调用Activity.startActivityForResult方法,最终会转到mInstrumentation.execStartActivity方法。由于Launcher自己处在一个单独的进程,所以它需要跨进程告诉系统服务我要启动App的需求。找到要通知的Service,名叫ActivityTaskManagerService,然后使用AIDL,通过Binder与他进行通信。

这里的简单说下ActivityTaskManagerService(简称ATMS)。原来这些通信工作都是属于ActivityManagerService,现在分了一部分工作给到ATMS,主要包括四大组件的调度工作。也是由SystemServer进程直接启动的,相关源码可见ActivityManagerService.Lifecycle.startService方法,感兴趣朋友可以自己看看。

接着说跨进程通信,相关代码如下:

  • //Instrumentation.java
  • int result = ActivityTaskManager.getService()
  • .startActivity(whoThread, who.getBasePackageName(), intent,
  • intent.resolveTypeIfNeeded(who.getContentResolver()),
  • token, target != null ? target.mEmbeddedID : null,
  • requestCode, 0, null, options);
  • //ActivityTaskManager.java
  • public static IActivityTaskManager getService() {
  • return IActivityTaskManagerSingleton.get();
  • }
  • private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton =
  • new Singleton<IActivityTaskManager>() {
  • @Override
  • protected IActivityTaskManager create() {
  • final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
  • return IActivityTaskManager.Stub.asInterface(b);
  • }
  • };
  • //ActivityTaskManagerService.java
  • public class ActivityTaskManagerService extends IActivityTaskManager.Stub
  • public static final class Lifecycle extends SystemService {
  • private final ActivityTaskManagerService mService;
  • public Lifecycle(Context context) {
  • super(context);
  • mService = new ActivityTaskManagerService(context);
  • }
  • @Override
  • public void onStart() {
  • publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService);
  • mService.start();
  • }
  • }
展开

startActivity我们都很熟悉,平时启动Activity都会使用,启动应用也是从这个方法开始的,也会同样带上intent信息,表示要启动的是哪个Activity。

另外要注意的一点是,startActivity之后有个checkStartActivityResult方法,这个方法是用作检查启动Activity的结果。当启动Activity失败的时候,就会通过这个方法抛出异常,比如有我们常见的问题:未在AndroidManifest.xml注册。

  • public static void checkStartActivityResult(int res, Object intent) {
  • switch (res) {
  • case ActivityManager.START_INTENT_NOT_RESOLVED:
  • case ActivityManager.START_CLASS_NOT_FOUND:
  • if (intent instanceof Intent && ((Intent)intent).getComponent() != null)
  • throw new ActivityNotFoundException(
  • "Unable to find explicit activity class "
  • + ((Intent)intent).getComponent().toShortString()
  • + "; have you declared this activity in your AndroidManifest.xml?");
  • throw new ActivityNotFoundException(
  • "No Activity found to handle " + intent);
  • case ActivityManager.START_PERMISSION_DENIED:
  • throw new SecurityException("Not allowed to start activity "
  • + intent);
  • case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
  • throw new AndroidRuntimeException(
  • "FORWARD_RESULT_FLAG used while also requesting a result");
  • case ActivityManager.START_NOT_ACTIVITY:
  • throw new IllegalArgumentException(
  • "PendingIntent is not an activity");
  • //...
  • }
  • }

第二关:通知Launcher可以休息了

ATMS收到要启动的消息后,就会通知上一个应用,也就是Launcher可以休息会了,进入Paused状态。

  • //ActivityStack.java
  • private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
  • //...
  • ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */);
  • //...
  • boolean pausing = getDisplay().pauseBackStacks(userLeaving, next, false);
  • if (mResumedActivity != null) {
  • if (DEBUG_STATES) Slog.d(TAG_STATES,
  • "resumeTopActivityLocked: Pausing " + mResumedActivity);
  • pausing |= startPausingLocked(userLeaving, false, next, false);
  • }
  • //...
  • if (next.attachedToProcess()) {
  • //应用已经启动
  • try {
  • //...
  • transaction.setLifecycleStateRequest(
  • ResumeActivityItem.obtain(next.app.getReportedProcState(),
  • getDisplay().mDisplayContent.isNextTransitionForward()));
  • mService.getLifecycleManager().scheduleTransaction(transaction);
  • //...
  • } catch (Exception e) {
  • //...
  • mStackSupervisor.startSpecificActivityLocked(next, true, false);
  • return true;
  • }
  • //...
  • // From this point on, if something goes wrong there is no way
  • // to recover the activity.
  • try {
  • next.completeResumeLocked();
  • } catch (Exception e) {
  • // If any exception gets thrown, toss away this
  • // activity and try the next one.
  • Slog.w(TAG, "Exception thrown during resume of " + next, e);
  • requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null,
  • "resume-exception", true);
  • return true;
  • }
  • } else {
  • //冷启动流程
  • mStackSupervisor.startSpecificActivityLocked(next, true, true);
  • }
  • }
展开

这里有两个类没有见过:

  • ActivityStack,是Activity的栈管理,相当于我们平时项目里面自己写的Activity管理类,用于管理Activity的状态啊,如栈出栈顺序等等。
  • ActivityRecord,代表具体的某一个Activity,存放了该Activity的各种信息。

startPausingLocked方法就是让上一个应用,这里也就是Launcher进入Paused状态。然后就会判断应用是否启动,如果已经启动了,就会走ResumeActivityItem的方法,看这个名字,结合应用已经启动的前提,是不是已经猜到了它是干吗的?没错,这个就是用来控制Activity的onResume生命周期方法的,不仅是onResume还有onStart方法,具体可见ActivityThread的handleResumeActivity方法源码。

如果应用没启动就会接着走到startSpecificActivityLocked方法,接着看。

第三关:是否已启动进程,否则创建进程

Launcher进入Paused之后,ActivityTaskManagerService就会判断要打开的这个应用进程是否已经启动,如果已经启动,则直接启动Activity即可,这也就是应用内的启动Activity流程。如果进程没有启动,则需要创建进程。

这里有两个问题:

  • 怎么判断应用进程是否存在呢?如果一个应用已经启动了,会在ATMS里面保存一个WindowProcessController信息,这个信息包括processName和uid,uid则是应用程序的id,可以通过applicationInfo.uid获取。processName则是进程名,一般为程序包名。所以判断是否存在应用进程,则是根据processName和uid去判断是否有对应的WindowProcessController,并且WindowProcessController里面的线程不为空。代码如下:
  • //ActivityStackSupervisor.java
  • void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {
  • // Is this activity's application already running?
  • final WindowProcessController wpc =
  • mService.getProcessController(r.processName, r.info.applicationInfo.uid);
  • boolean knownToBeDead = false;
  • if (wpc != null && wpc.hasThread()) {
  • //应用进程存在
  • try {
  • realStartActivityLocked(r, wpc, andResume, checkConfig);
  • return;
  • }
  • }
  • }
  • //WindowProcessController.java
  • IApplicationThread getThread() {
  • return mThread;
  • }
  • boolean hasThread() {
  • return mThread != null;
  • }
  • 还有个问题就是怎么创建进程?还记得Z老师吗?对,就是Zygote进程。之前说了他是所有进程的父进程,所以就要通知Zygote去fork一个新的进程,服务于这个应用。
  • //ZygoteProcess.java
  • private Process.ProcessStartResult attemptUsapSendArgsAndGetResult(
  • ZygoteState zygoteState, String msgStr)
  • throws ZygoteStartFailedEx, IOException {
  • try (LocalSocket usapSessionSocket = zygoteState.getUsapSessionSocket()) {
  • final BufferedWriter usapWriter =
  • new BufferedWriter(
  • new OutputStreamWriter(usapSessionSocket.getOutputStream()),
  • Zygote.SOCKET_BUFFER_SIZE);
  • final DataInputStream usapReader =
  • new DataInputStream(usapSessionSocket.getInputStream());
  • usapWriter.write(msgStr);
  • usapWriter.flush();
  • Process.ProcessStartResult result = new Process.ProcessStartResult();
  • result.pid = usapReader.readInt();
  • // USAPs can't be used to spawn processes that need wrappers.
  • result.usingWrapper = false;
  • if (result.pid >= 0) {
  • return result;
  • } else {
  • throw new ZygoteStartFailedEx("USAP specialization failed");
  • }
  • }
  • }

可以看到,这里其实是通过socket和Zygote进行通信,BufferedWriter用于读取和接收消息。这里将要新建进程的消息传递给Zygote,由Zygote进行fork进程,并返回新进程的pid。

可能又会有人问了?fork是啥?为啥这里又变成socket进行IPC通信,而不是Bindler了?

  • 首先,fork()是一个方法,是类Unix操作系统上创建进程的主要方法。用于创建子进程(等同于当前进程的副本)。
  • 那为什么fork的时候不用Binder而用socket了呢?主要是因为fork不允许存在多线程,Binder通讯偏偏就是多线程。

问题总是在不断产生,总有好奇的朋友会接着问,为什么fork不允许存在多线程?

  • 防止死锁。其实你想想,多线程+多进程,听起就不咋靠谱是不。假设多线程里面线程A对某个锁lock,另外一个线程B调用fork创建了子进程,但是子进程却没有了线程A,但是锁本身却被fork了出来,那么这个锁没人可以打开了。一旦子进程中另外的线程又对这个锁进行lock,就死锁了。

第四关:ActivityThread闪亮登场

刚才说到由Zygote进行fork进程,并返回新进程的pid。其实这过程中也实例化ActivityThread对象。一起看看是怎么实现的:

  • //RuntimeInit.java
  • protected static Runnable findStaticMain(String className, String[] argv,
  • ClassLoader classLoader) {
  • Class<?> cl;
  • try {
  • cl = Class.forName(className, true, classLoader);
  • } catch (ClassNotFoundException ex) {
  • throw new RuntimeException(
  • "Missing class when invoking static main " + className,
  • ex);
  • }
  • Method m;
  • try {
  • m = cl.getMethod("main", new Class[] { String[].class });
  • } catch (NoSuchMethodException ex) {
  • throw new RuntimeException(
  • "Missing static main on " + className, ex);
  • } catch (SecurityException ex) {
  • throw new RuntimeException(
  • "Problem getting static main on " + className, ex);
  • }
  • //...
  • return new MethodAndArgsCaller(m, argv);
  • }

原来是反射!通过反射调用了ActivityThread 的 main 方法。ActivityThread大家应该都很熟悉了,代表了Android的主线程,而main方法也是app的主入口。这不对上了!新建进程的时候就调用了,可不是主入口嘛。来看看这个主入口。

  • public static void main(String[] args) {
  • //...
  • Looper.prepareMainLooper();
  • ActivityThread thread = new ActivityThread();
  • thread.attach(false, startSeq);
  • //...
  • if (false) {
  • Looper.myLooper().setMessageLogging(new
  • LogPrinter(Log.DEBUG, "ActivityThread"));
  • }
  • //...
  • Looper.loop();
  • throw new RuntimeException("Main thread loop unexpectedly exited");
  • }

main方法主要创建了ActivityThread,创建了主线程的Looper对象,并开始loop循环。除了这些,还要告诉AMS,我醒啦,进程创建好了!也就是上述代码中的attach方法,最后会转到AMSattachApplicationLocked方法,一起看看这个方法干了啥:

  • //ActivitymanagerService.java
  • private final boolean attachApplicationLocked(IApplicationThread thread,
  • int pid, int callingUid, long startSeq) {
  • //...
  • ProcessRecord app;
  • //...
  • thread.bindApplication(processName, appInfo, providers, null, profilerInfo,
  • null, null, null, testMode,
  • mBinderTransactionTrackingEnabled, enableTrackAllocation,
  • isRestrictedBackupMode || !normalMode, app.isPersistent(),
  • new Configuration(app.getWindowProcessController().getConfiguration()),
  • app.compat, getCommonServicesLocked(app.isolated),
  • mCoreSettingsObserver.getCoreSettingsLocked(),
  • buildSerial, autofillOptions, contentCaptureOptions);
  • //...
  • app.makeActive(thread, mProcessStats);
  • //...
  • // See if the top visible activity is waiting to run in this process...
  • if (normalMode) {
  • try {
  • didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
  • } catch (Exception e) {
  • Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
  • badApp = true;
  • }
  • }
  • //...
  • }
  • //ProcessRecord.java
  • public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) {
  • //...
  • thread = _thread;
  • mWindowProcessController.setThread(thread);
  • }
展开

这里主要做了三件事:

  • bindApplication方法,主要用来启动Application。
  • makeActive方法,设定WindowProcessController里面的线程,也就是上文中说过判断进程是否存在所用到的。
  • attachApplication方法,启动根Activity。

第五关:创建Application

接着上面看,按照我们所熟知的,应用启动后,应该就是启动Applicaiton,启动Activity。看看是不是怎么回事:

  • //ActivityThread#ApplicationThread
  • 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, AutofillOptions autofillOptions,
  • ContentCaptureOptions contentCaptureOptions) {
  • 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;
  • data.autofillOptions = autofillOptions;
  • data.contentCaptureOptions = contentCaptureOptions;
  • sendMessage(H.BIND_APPLICATION, data);
  • }
  • public void handleMessage(Message msg) {
  • if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
  • switch (msg.what) {
  • case BIND_APPLICATION:
  • Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
  • AppBindData data = (AppBindData)msg.obj;
  • handleBindApplication(data);
  • Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
  • break;
  • }
  • }
展开

可以看到这里有个H,H是主线程的一个Handler类,用于处理需要主线程处理的各类消息,包括BIND_SERVICE,LOW_MEMORY,DUMP_HEAP等等。接着看handleBindApplication:

  • private void handleBindApplication(AppBindData data) {
  • //...
  • try {
  • final ClassLoader cl = instrContext.getClassLoader();
  • mInstrumentation = (Instrumentation)
  • cl.loadClass(data.instrumentationName.getClassName()).newInstance();
  • }
  • //...
  • Application app;
  • final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
  • final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();
  • try {
  • // If the app is being launched for full backup or restore, bring it up in
  • // a restricted environment with the base application class.
  • 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
  • if (!data.restrictedBackupMode) {
  • if (!ArrayUtils.isEmpty(data.providers)) {
  • installContentProviders(app, data.providers);
  • }
  • }
  • // Do this after providers, since instrumentation tests generally start their
  • // test thread at this point, and we don't want that racing.
  • try {
  • mInstrumentation.onCreate(data.instrumentationArgs);
  • }
  • //...
  • 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);
  • }
  • }
  • }
  • //...
  • }
展开

这里信息量就多了,一点点的看:

  • 首先,创建了Instrumentation,也就是上文一开始startActivity的第一步。每个应用程序都有一个Instrumentation,用于管理这个进程,比如要创建Activity的时候,首先就会执行到这个类里面。
  • makeApplication方法,创建了Application,终于到这一步了。最终会走到newApplication方法,执行Application的attach方法。
  • public Application newApplication(ClassLoader cl, String className, Context context)
  • throws InstantiationException, IllegalAccessException,
  • ClassNotFoundException {
  • Application app = getFactory(context.getPackageName())
  • .instantiateApplication(cl, className);
  • app.attach(context);
  • return app;
  • }

attach方法有了,onCreate方法又是何时调用的呢?马上来了:

  • instrumentation.callApplicationOnCreate(app);
  • public void callApplicationOnCreate(Application app) {
  • app.onCreate();
  • }

也就是创建Application->attach->onCreate调用顺序。

等等,在onCreate之前还有一句重要的代码:

  • installContentProviders

这里就是启动Provider的相关代码了,具体逻辑就不分析了。

第六关:启动Activity

说完bindApplication,该说说后续了,上文第五关说到,bindApplication方法之后执行的是attachApplication方法,最终会执行到ActivityThread的handleLaunchActivity方法:

  • public Activity handleLaunchActivity(ActivityClientRecord r,
  • PendingTransactionActions pendingActions, Intent customIntent) {
  • //...
  • WindowManagerGlobal.initialize();
  • //...
  • final Activity a = performLaunchActivity(r, customIntent);
  • //...
  • return a;
  • }

首先,初始化了WindowManagerGlobal,这是个啥呢?没错,就是WindowManagerService了,也为后续窗口显示等作了准备。

继续看performLaunchActivity:

  • private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
  • //创建ContextImpl
  • ContextImpl appContext = createBaseContextForActivity(r);
  • Activity activity = null;
  • try {
  • java.lang.ClassLoader cl = appContext.getClassLoader();
  • //创建Activity
  • activity = mInstrumentation.newActivity(
  • cl, component.getClassName(), r.intent);
  • }
  • try {
  • if (activity != null) {
  • //完成activity的一些重要数据的初始化
  • activity.attach(appContext, this, getInstrumentation(), r.token,
  • r.ident, app, r.intent, r.activityInfo, title, r.parent,
  • r.embeddedID, r.lastNonConfigurationInstances, config,
  • r.referrer, r.voiceInteractor, window, r.configCallback,
  • r.assistToken);
  • if (customIntent != null) {
  • activity.mIntent = customIntent;
  • }
  • //设置activity的主题
  • int theme = r.activityInfo.getThemeResource();
  • if (theme != 0) {
  • activity.setTheme(theme);
  • }
  • //调用activity的onCreate方法
  • if (r.isPersistable()) {
  • mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
  • } else {
  • mInstrumentation.callActivityOnCreate(activity, r.state);
  • }
  • }
  • }
  • return activity;
  • }
展开

哇,终于看到onCreate方法了。稳住,还是一步步看看这段代码。

首先,创建了ContextImpl对象,ContextImpl可能有的朋友不知道是啥,ContextImpl继承自Context,其实就是我们平时用的上下文。有的同学可能表示,这不对啊,获取上下文明明获取的是Context对象。来一起跟随源码看看。

  • //Activity.java
  • Context mBase;
  • @Override
  • public Executor getMainExecutor() {
  • return mBase.getMainExecutor();
  • }
  • @Override
  • public Context getApplicationContext() {
  • return mBase.getApplicationContext();
  • }

这里可以看到,我们平时用的上下文就是这个mBase,那么找到这个mBase是啥就行了:

  • protected void attachBaseContext(Context base) {
  • if (mBase != null) {
  • throw new IllegalStateException("Base context already set");
  • }
  • mBase = base;
  • }
  • //一层层往上找
  • final void attach(Context context, ActivityThread aThread,
  • Instrumentation instr, IBinder token, int ident,
  • Application application, Intent intent, ActivityInfo info,
  • CharSequence title, Activity parent, String id,
  • NonConfigurationInstances lastNonConfigurationInstances,
  • Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
  • attachBaseContext(context);
  • mWindow = new PhoneWindow(this, window, activityConfigCallback);
  • mWindow.setWindowControllerCallback(this);
  • mWindow.setCallback(this);
  • mWindow.setOnWindowDismissedCallback(this);
  • mWindow.getLayoutInflater().setPrivateFactory(this);
  • if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
  • mWindow.setSoftInputMode(info.softInputMode);
  • }
  • }

这不就是,,,刚才一开始performLaunchActivity方法里面的attach吗?太巧了,所以这个ContextImpl就是我们平时所用的上下文。

顺便看看attach还干了啥?新建了PhoneWindow,建立自己和Window的关联,并设置了setSoftInputMode等等。

ContextImpl创建完之后,会通过类加载器创建Activity的对象,然后设置好activity的主题,最后调用了activity的onCreate方法。

总结

再一起捋一遍App的启动流程:

  • Launcher被调用点击事件,转到Instrumentation类的startActivity方法。
  • Instrumentation通过跨进程通信告诉AMS要启动应用的需求。
  • AMS反馈Launcher,让Launcher进入Paused状态
  • Launcher进入Paused状态,AMS转到ZygoteProcess类,并通过socket与Zygote通信,告知Zygote需要新建进程。
  • Zygote fork进程,并调用ActivityThread的main方法,也就是app的入口。
  • ActivityThread的main方法新建了ActivityThread实例,并新建了Looper实例,开始loop循环。
  • 同时ActivityThread也告知AMS,进程创建完毕,开始创建Application,Provider,并调用Applicaiton的attach,onCreate方法。
  • 最后就是创建上下文,通过类加载器加载Activity,调用Activity的onCreate方法。

至此,应用启动完毕。

当然,分析源码的目的一直都不是为了学知识而学,而是理解了这些基础,我们才能更好的解决问题。学习了App的启动流程,我们可以再思考下一些之前没理解透的问题,比如启动优化

分析启动过程,其实可以优化启动速度的地方有三个地方:

  • Application的attach方法,MultiDexApplication会在方法里面会去执行MultiDex逻辑。所以这里可以进行MultiDex优化,比如今日头条方案就是单独启动一个进程的activity去加载MultiDex。
  • Application的onCreate方法,大量三方库的初始化都在这里进行,所以我们可以开启线程池,懒加载等等。把每个启动任务进行区分,哪些可以子线程运行,哪些有先后顺序。
  • Activity的onCreate方法,同样进行线程处理,懒加载。或者预创建Activity,提前类加载等等。

最后希望各位老铁都能有一个乖巧可爱漂亮的女儿/儿子

Android开发者们,快来关注公众号【码上积木】,每天三问面试题,并详细剖析,助你成为offer收割机。

积累也是一种力量。

本站文章资源均来源自网络,除非特别声明,否则均不代表站方观点,并仅供查阅,不作为任何参考依据!
如有侵权请及时跟我们联系,本站将及时删除!
如遇版权问题,请查看 本站版权声明
THE END
分享
二维码
海报
<<上一篇
下一篇>>