6岁女儿问我:APP是如何启动的?

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

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

[[346936]]

图片来自 Pexels

我心里一惊:

[[346937]]

 

首先,new 一个女儿:

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

小天才电话手表的系统就是 Android,所以这不就是面试官常考的应用启动流程嘛!

女儿也要来面试我了吗?😭好了,既然女儿问了,那就答吧。但是,对付这个小小的 0 经验面试官,我该咋说呢?

解答小小面试官

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

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

[[346938]]

 

应用启动过程就像一个小朋友被叫醒一样,开机之后呢,Z 老师会依次叫醒班长和班干部(SystemServer#ActivityManagerService,Launcher)。

小 L 醒了之后就会去了解手表里有哪些小朋友,长什么样(icon,name),家庭信息(包名,androidmanifest)等等,然后一个个把小朋友的照片(icon)贴到自己的身上。

比如有音乐小朋友,聊天小朋友,日历小朋友,其实也就是你手表上这个桌面啦。

这时候你要点开一个音乐小朋友呢(startActivity),小 L 就会通知班长小 A(Binder)。

小 A 知道了之后,让小 L 自己休息下(Paused),然后就去找 Z 老师了。Z 老师就负责叫音乐小朋友起床了(fork 进程,启动 ActivityThread)。

音乐小朋友起来后就又找小 A 带她去洗脸刷牙(启动ApplicationThread,Activity)。

都弄完了就可以进行各种表演了,唱歌啊,跳舞啊。

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

 

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

十五年后

  1. mDdaughter.grow(15) 
  2. 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 方法,感兴趣朋友可以自己看看。

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

  1. //Instrumentation.java 
  2.    int result = ActivityTaskManager.getService() 
  3.                .startActivity(whoThread, who.getBasePackageName(), intent, 
  4.                        intent.resolveTypeIfNeeded(who.getContentResolver()), 
  5.                        token, target != null ? target.mEmbeddedID : null
  6.                        requestCode, 0, null, options); 
  7.  
  8.  
  9.    //ActivityTaskManager.java             
  10.    public static IActivityTaskManager getService() { 
  11.        return IActivityTaskManagerSingleton.get(); 
  12.    } 
  13.    private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton = 
  14.            new Singleton<IActivityTaskManager>() { 
  15.                @Override 
  16.                protected IActivityTaskManager create() { 
  17.                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE); 
  18.                    return IActivityTaskManager.Stub.asInterface(b); 
  19.                } 
  20.            }; 
  21.  
  22.  
  23.  
  24.    //ActivityTaskManagerService.java 
  25.    public class ActivityTaskManagerService extends IActivityTaskManager.Stub 
  26.  
  27.    public static final class Lifecycle extends SystemService { 
  28.        private final ActivityTaskManagerService mService; 
  29.  
  30.        public Lifecycle(Context context) { 
  31.            super(context); 
  32.            mService = new ActivityTaskManagerService(context); 
  33.        } 
  34.  
  35.        @Override 
  36.        public void onStart() { 
  37.            publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService); 
  38.            mService.start(); 
  39.        } 
  40.    }     

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

另外要注意的一点是,startActivity 之后有个 checkStartActivityResult 方法,这个方法是用作检查启动 Activity 的结果。

当启动 Activity 失败的时候,就会通过这个方法抛出异常,比如有我们常见的问题:未在 AndroidManifest.xml 注册。

  1. public static void checkStartActivityResult(int res, Object intent) { 
  2.         switch (res) { 
  3.             case ActivityManager.START_INTENT_NOT_RESOLVED: 
  4.             case ActivityManager.START_CLASS_NOT_FOUND: 
  5.                 if (intent instanceof Intent && ((Intent)intent).getComponent() != null
  6.                     throw new ActivityNotFoundException( 
  7.                             "Unable to find explicit activity class " 
  8.                             + ((Intent)intent).getComponent().toShortString() 
  9.                             + "; have you declared this activity in your AndroidManifest.xml?"); 
  10.                 throw new ActivityNotFoundException( 
  11.                         "No Activity found to handle " + intent); 
  12.             case ActivityManager.START_PERMISSION_DENIED: 
  13.                 throw new SecurityException("Not allowed to start activity " 
  14.                         + intent); 
  15.             case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT: 
  16.                 throw new AndroidRuntimeException( 
  17.                         "FORWARD_RESULT_FLAG used while also requesting a result"); 
  18.             case ActivityManager.START_NOT_ACTIVITY: 
  19.                 throw new IllegalArgumentException( 
  20.                         "PendingIntent is not an activity"); 
  21.             //... 
  22.         } 
  23.     }  

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

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

  1. //ActivityStack.java 
  2.  
  3.     private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) { 
  4.         //... 
  5.         ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */); 
  6.         //... 
  7.         boolean pausing = getDisplay().pauseBackStacks(userLeaving, nextfalse); 
  8.         if (mResumedActivity != null) { 
  9.             if (DEBUG_STATES) Slog.d(TAG_STATES, 
  10.                     "resumeTopActivityLocked: Pausing " + mResumedActivity); 
  11.             pausing |= startPausingLocked(userLeaving, falsenextfalse); 
  12.         } 
  13.         //... 
  14.  
  15.         if (next.attachedToProcess()) { 
  16.             //应用已经启动 
  17.             try { 
  18.                 //... 
  19.                 transaction.setLifecycleStateRequest( 
  20.                         ResumeActivityItem.obtain(next.app.getReportedProcState(), 
  21.                                 getDisplay().mDisplayContent.isNextTransitionForward())); 
  22.                 mService.getLifecycleManager().scheduleTransaction(transaction); 
  23.                 //... 
  24.             } catch (Exception e) { 
  25.                 //... 
  26.                 mStackSupervisor.startSpecificActivityLocked(nexttruefalse); 
  27.                 return true
  28.             } 
  29.             //... 
  30.             // From this point on, if something goes wrong there is no way 
  31.             // to recover the activity. 
  32.             try { 
  33.                 next.completeResumeLocked(); 
  34.             } catch (Exception e) { 
  35.                 // If any exception gets thrown, toss away this 
  36.                 // activity and try the next one. 
  37.                 Slog.w(TAG, "Exception thrown during resume of " + next, e); 
  38.                 requestFinishActivityLocked(next.appToken, Activity.RESULT_CANCELED, null
  39.                         "resume-exception"true); 
  40.                 return true
  41.             } 
  42.         } else { 
  43.             //冷启动流程 
  44.             mStackSupervisor.startSpecificActivityLocked(nexttruetrue); 
  45.         }         
  46.     } 

这里有两个类没有见过:

  • 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 里面的线程不为空。

代码如下:

  1. //ActivityStackSupervisor.java 
  2.    void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) { 
  3.        // Is this activity's application already running? 
  4.        final WindowProcessController wpc = 
  5.                mService.getProcessController(r.processName, r.info.applicationInfo.uid); 
  6.  
  7.        boolean knownToBeDead = false
  8.        if (wpc != null && wpc.hasThread()) { 
  9.            //应用进程存在 
  10.            try { 
  11.                realStartActivityLocked(r, wpc, andResume, checkConfig); 
  12.                return
  13.            }  
  14.        } 
  15.    } 
  16.  
  17.    //WindowProcessController.java 
  18.    IApplicationThread getThread() { 
  19.        return mThread; 
  20.    } 
  21.  
  22.    boolean hasThread() { 
  23.        return mThread != null
  24.    } 

②还有个问题就是怎么创建进程?还记得 Z 老师吗?对,就是 Zygote 进程。之前说了他是所有进程的父进程,所以就要通知 Zygote 去 fork 一个新的进程,服务于这个应用。

  1. //ZygoteProcess.java 
  2.    private Process.ProcessStartResult attemptUsapSendArgsAndGetResult( 
  3.            ZygoteState zygoteState, String msgStr) 
  4.            throws ZygoteStartFailedEx, IOException { 
  5.        try (LocalSocket usapSessionSocket = zygoteState.getUsapSessionSocket()) { 
  6.            final BufferedWriter usapWriter = 
  7.                    new BufferedWriter( 
  8.                            new OutputStreamWriter(usapSessionSocket.getOutputStream()), 
  9.                            Zygote.SOCKET_BUFFER_SIZE); 
  10.            final DataInputStream usapReader = 
  11.                    new DataInputStream(usapSessionSocket.getInputStream()); 
  12.  
  13.            usapWriter.write(msgStr); 
  14.            usapWriter.flush(); 
  15.  
  16.            Process.ProcessStartResult result = new Process.ProcessStartResult(); 
  17.            result.pid = usapReader.readInt(); 
  18.            // USAPs can't be used to spawn processes that need wrappers. 
  19.            result.usingWrapper = false
  20.  
  21.            if (result.pid >= 0) { 
  22.                return result; 
  23.            } else { 
  24.                throw new ZygoteStartFailedEx("USAP specialization failed"); 
  25.            } 
  26.        } 
  27.    } 

可以看到,这里其实是通过 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 对象。

一起看看是怎么实现的:

  1. //RuntimeInit.java 
  2.    protected static Runnable findStaticMain(String className, String[] argv, 
  3.             ClassLoader classLoader) { 
  4.         Class<?> cl; 
  5.  
  6.         try { 
  7.             cl = Class.forName(className, true, classLoader); 
  8.         } catch (ClassNotFoundException ex) { 
  9.             throw new RuntimeException( 
  10.                     "Missing class when invoking static main " + className, 
  11.                     ex); 
  12.         } 
  13.  
  14.         Method m; 
  15.         try { 
  16.             m = cl.getMethod("main", new Class[] { String[].class }); 
  17.         } catch (NoSuchMethodException ex) { 
  18.             throw new RuntimeException( 
  19.                     "Missing static main on " + className, ex); 
  20.         } catch (SecurityException ex) { 
  21.             throw new RuntimeException( 
  22.                     "Problem getting static main on " + className, ex); 
  23.         } 
  24.         //... 
  25.         return new MethodAndArgsCaller(m, argv); 
  26.     } 

原来是反射!通过反射调用了 ActivityThread 的 main 方法。

ActivityThread 大家应该都很熟悉了,代表了 Android 的主线程,而 main 方法也是 App 的主入口。

这不对上了!新建进程的时候就调用了,可不是主入口嘛。来看看这个主入口。

  1. public static void main(String[] args) { 
  2.         //... 
  3.         Looper.prepareMainLooper(); 
  4.  
  5.         ActivityThread thread = new ActivityThread(); 
  6.         thread.attach(false, startSeq); 
  7.  
  8.         //... 
  9.  
  10.         if (false) { 
  11.             Looper.myLooper().setMessageLogging(new 
  12.                     LogPrinter(Log.DEBUG, "ActivityThread")); 
  13.         } 
  14.         //... 
  15.         Looper.loop(); 
  16.  
  17.         throw new RuntimeException("Main thread loop unexpectedly exited"); 
  18.     } 

main 方法主要创建了 ActivityThread,创建了主线程的 Looper 对象,并开始 loop 循环。

除了这些,还要告诉 AMS,我醒啦,进程创建好了!也就是上述代码中的 attach 方法,最后会转到 AMSattachApplicationLocked 方法。

一起看看这个方法干了啥:

  1. //ActivitymanagerService.java 
  2.    private final boolean attachApplicationLocked(IApplicationThread thread, 
  3.            int pid, int callingUid, long startSeq) { 
  4.        //... 
  5.        ProcessRecord app; 
  6.        //... 
  7.        thread.bindApplication(processName, appInfo, providers, null, profilerInfo, 
  8.                        nullnullnull, testMode, 
  9.                        mBinderTransactionTrackingEnabled, enableTrackAllocation, 
  10.                        isRestrictedBackupMode || !normalMode, app.isPersistent(), 
  11.                        new Configuration(app.getWindowProcessController().getConfiguration()), 
  12.                        app.compat, getCommonServicesLocked(app.isolated), 
  13.                        mCoreSettingsObserver.getCoreSettingsLocked(), 
  14.                        buildSerial, autofillOptions, contentCaptureOptions); 
  15.        //... 
  16.        app.makeActive(thread, mProcessStats); 
  17.  
  18.        //... 
  19.        // See if the top visible activity is waiting to run in this process... 
  20.        if (normalMode) { 
  21.            try { 
  22.                didSomething = mAtmInternal.attachApplication(app.getWindowProcessController()); 
  23.            } catch (Exception e) { 
  24.                Slog.wtf(TAG, "Exception thrown launching activities in " + app, e); 
  25.                badApp = true
  26.            } 
  27.        } 
  28.        //...         
  29.    } 
  30.  
  31.    //ProcessRecord.java 
  32.    public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) { 
  33.        //... 
  34.        thread = _thread; 
  35.        mWindowProcessController.setThread(thread); 
  36.    } 

这里主要做了三件事:

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

第五关:创建 Application

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

看看是不是怎么回事:

  1. //ActivityThread#ApplicationThread 
  2.     public final void bindApplication(String processName, ApplicationInfo appInfo, 
  3.                 List<ProviderInfo> providers, ComponentName instrumentationName, 
  4.                 ProfilerInfo profilerInfo, Bundle instrumentationArgs, 
  5.                 IInstrumentationWatcher instrumentationWatcher, 
  6.                 IUiAutomationConnection instrumentationUiConnection, int debugMode, 
  7.                 boolean enableBinderTracking, boolean trackAllocation, 
  8.                 boolean isRestrictedBackupMode, boolean persistent, Configuration config, 
  9.                 CompatibilityInfo compatInfo, Map services, Bundle coreSettings, 
  10.                 String buildSerial, AutofillOptions autofillOptions, 
  11.                 ContentCaptureOptions contentCaptureOptions) { 
  12.             AppBindData data = new AppBindData(); 
  13.             data.processName = processName; 
  14.             data.appInfo = appInfo; 
  15.             data.providers = providers; 
  16.             data.instrumentationName = instrumentationName; 
  17.             data.instrumentationArgs = instrumentationArgs; 
  18.             data.instrumentationWatcher = instrumentationWatcher; 
  19.             data.instrumentationUiAutomationConnection = instrumentationUiConnection; 
  20.             data.debugMode = debugMode; 
  21.             data.enableBinderTracking = enableBinderTracking; 
  22.             data.trackAllocation = trackAllocation; 
  23.             data.restrictedBackupMode = isRestrictedBackupMode; 
  24.             data.persistent = persistent; 
  25.             data.config = config; 
  26.             data.compatInfo = compatInfo; 
  27.             data.initProfilerInfo = profilerInfo; 
  28.             data.buildSerial = buildSerial; 
  29.             data.autofillOptions = autofillOptions; 
  30.             data.contentCaptureOptions = contentCaptureOptions; 
  31.             sendMessage(H.BIND_APPLICATION, data); 
  32.         } 
  33.  
  34.         public void handleMessage(Message msg) { 
  35.             if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); 
  36.             switch (msg.what) { 
  37.                 case BIND_APPLICATION: 
  38.                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication"); 
  39.                     AppBindData data = (AppBindData)msg.obj; 
  40.                     handleBindApplication(data); 
  41.                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); 
  42.                     break; 
  43.                 } 
  44.             } 

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

接着看 handleBindApplication:

  1. private void handleBindApplication(AppBindData data) { 
  2.         //... 
  3.         try { 
  4.             final ClassLoader cl = instrContext.getClassLoader(); 
  5.             mInstrumentation = (Instrumentation) 
  6.                     cl.loadClass(data.instrumentationName.getClassName()).newInstance(); 
  7.         } 
  8.         //... 
  9.         Application app; 
  10.         final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites(); 
  11.         final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy(); 
  12.         try { 
  13.             // If the app is being launched for full backup or restore, bring it up in 
  14.             // a restricted environment with the base application class. 
  15.             app = data.info.makeApplication(data.restrictedBackupMode, null); 
  16.             mInitialApplication = app; 
  17.             // don't bring up providers in restricted mode; they may depend on the 
  18.             // app's custom Application class 
  19.             if (!data.restrictedBackupMode) { 
  20.                 if (!ArrayUtils.isEmpty(data.providers)) { 
  21.                     installContentProviders(app, data.providers); 
  22.                 } 
  23.             } 
  24.  
  25.             // Do this after providers, since instrumentation tests generally start their 
  26.             // test thread at this point, and we don't want that racing. 
  27.             try { 
  28.                 mInstrumentation.onCreate(data.instrumentationArgs); 
  29.             } 
  30.             //... 
  31.             try { 
  32.                 mInstrumentation.callApplicationOnCreate(app); 
  33.             } catch (Exception e) { 
  34.                 if (!mInstrumentation.onException(app, e)) { 
  35.                     throw new RuntimeException( 
  36.                             "Unable to create application " + app.getClass().getName() 
  37.                                     + ": " + e.toString(), e); 
  38.                 } 
  39.             } 
  40.         } 
  41.             //... 
  42.     } 

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

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

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

  1. instrumentation.callApplicationOnCreate(app); 
  2.  
  3.     public void callApplicationOnCreate(Application app) { 
  4.         app.onCreate(); 
  5.     } 

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

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

  1. installContentProviders 

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

第六关:启动 Activity

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

  1. public Activity handleLaunchActivity(ActivityClientRecord r, 
  2.                                          PendingTransactionActions pendingActions, Intent customIntent) { 
  3.         //... 
  4.         WindowManagerGlobal.initialize(); 
  5.         //... 
  6.         final Activity a = performLaunchActivity(r, customIntent); 
  7.         //... 
  8.         return a; 
  9.     } 

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

继续看 performLaunchActivity:

  1. private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { 
  2.         //创建ContextImpl 
  3.         ContextImpl appContext = createBaseContextForActivity(r); 
  4.         Activity activity = null
  5.         try { 
  6.             java.lang.ClassLoader cl = appContext.getClassLoader(); 
  7.             //创建Activity 
  8.             activity = mInstrumentation.newActivity( 
  9.                     cl, component.getClassName(), r.intent); 
  10.         } 
  11.  
  12.         try { 
  13.             if (activity != null) { 
  14.                 //完成activity的一些重要数据的初始化 
  15.                 activity.attach(appContext, this, getInstrumentation(), r.token, 
  16.                         r.ident, app, r.intent, r.activityInfo, title, r.parent, 
  17.                         r.embeddedID, r.lastNonConfigurationInstances, config, 
  18.                         r.referrer, r.voiceInteractor, window, r.configCallback, 
  19.                         r.assistToken); 
  20.  
  21.                 if (customIntent != null) { 
  22.                     activity.mIntent = customIntent; 
  23.                 } 
  24.  
  25.                 //设置activity的主题 
  26.                 int theme = r.activityInfo.getThemeResource(); 
  27.                 if (theme != 0) { 
  28.                     activity.setTheme(theme); 
  29.                 } 
  30.  
  31.                 //调用activity的onCreate方法 
  32.                 if (r.isPersistable()) { 
  33.                     mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState); 
  34.                 } else { 
  35.                     mInstrumentation.callActivityOnCreate(activity, r.state); 
  36.                 } 
  37.             } 
  38.         } 
  39.  
  40.         return activity; 
  41.     } 

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

首先,创建了 ContextImpl 对象,ContextImpl 可能有的朋友不知道是啥,ContextImpl 继承自 Context,其实就是我们平时用的上下文。

有的同学可能表示,这不对啊,获取上下文明明获取的是 Context 对象。来一起跟随源码看看。

  1. //Activity.java 
  2.     Context mBase; 
  3.  
  4.     @Override 
  5.     public Executor getMainExecutor() { 
  6.         return mBase.getMainExecutor(); 
  7.     } 
  8.  
  9.     @Override 
  10.     public Context getApplicationContext() { 
  11.         return mBase.getApplicationContext(); 
  12.     } 

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

  1. protected void attachBaseContext(Context base) { 
  2.         if (mBase != null) { 
  3.             throw new IllegalStateException("Base context already set"); 
  4.         } 
  5.         mBase = base; 
  6.     } 
  7.  
  8.     //一层层往上找 
  9.  
  10.     final void attach(Context context, ActivityThread aThread, 
  11.             Instrumentation instr, IBinder token, int ident, 
  12.             Application application, Intent intent, ActivityInfo info, 
  13.             CharSequence title, Activity parent, String id, 
  14.             NonConfigurationInstances lastNonConfigurationInstances, 
  15.             Configuration config, String referrer, IVoiceInteractor voiceInteractor) { 
  16.  
  17.         attachBaseContext(context); 
  18.  
  19.         mWindow = new PhoneWindow(this, window, activityConfigCallback); 
  20.         mWindow.setWindowControllerCallback(this); 
  21.         mWindow.setCallback(this); 
  22.         mWindow.setOnWindowDismissedCallback(this); 
  23.         mWindow.getLayoutInflater().setPrivateFactory(this); 
  24.         if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) { 
  25.             mWindow.setSoftInputMode(info.softInputMode); 
  26.         } 
  27.  
  28.  
  29.     } 

这不就是刚才一开始 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,提前类加载等等。

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

作者:积木zz

编辑:陶家龙

出处:转载自公众号码上积木(ID:Lzjimu)

责任编辑:武晓燕 来源: 码上积木
相关推荐

2021-01-05 08:59:01

InnoDB数据磁盘

2014-03-17 15:16:02

移动开发iOS应用

2020-01-19 09:25:58

失独AI技术

2019-01-22 15:26:48

APP会员自动续费签约

2021-11-12 11:51:03

基于内容的推荐查询推荐

2013-04-28 15:56:42

儿童

2021-06-28 14:35:36

iOSAPP缓存

2019-10-21 17:11:47

程序员不完美妈妈跳槽那些事儿

2019-08-20 14:23:26

深度学习编程人工智能

2022-02-09 09:37:54

ReactorNettyI/O

2021-11-23 10:50:29

关联规则推荐推荐系统开发

2021-11-15 12:45:44

协同过滤算法架构

2021-03-01 08:54:39

开发双亲委派

2019-12-30 09:03:49

职业阿里云网络

2019-12-23 11:03:07

抽象MOVJava

2019-08-15 16:30:49

TomcatSpringBootJava

2021-01-07 08:57:14

MySQL数据库索引

2020-05-26 16:27:58

函数孩子编程

2020-05-26 21:17:28

函数式编程纯函数

2022-07-05 14:49:25

Redis 6多线程
点赞
收藏

51CTO技术栈公众号