女儿拿着小天才电话手表问我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收割机。
积累也是一种力量。