鱼乐程序员@IT·互联网

Android应用启动流程分析

2017-09-07  本文已影响848人  android之子

1.应用启动流程分析:

1.1流程

一个Activity的启动过程。以Launcher中启动一个App为例,比如在Launcher中我们点了一个图标启动一个App的Activity,Launcher里会执行:

Intent intent = new Intent("xxx");

startActivity(intent);

简单的说下大体流程,App与AMS交互流程主要分以下几步:

1. 原App通知AMS要起一个新Activity。

2. AMS创建相应数据结构,然后通知WMS创建相应数据结构,再通知原Activity暂停。

3. 原Activity暂停后通知AMS。

4. AMS创建新App进程,通知WMS新App可见,再通知App创建Activity等相应数据结构。

首先分析下App端的结构。移动平台一般显示区域有限,要完成一个工作往往不是一屏内容中能搞定的,所以Android中有了Activity的概念,让用户可以把相关的子内容放到单独的Activity中,然后通过Intent在Activity间跳转。类似于浏览网页,点击链接跳转到另一个网页。这些同一交互过程中的一系列Activity成为一个Task。这些Activity运行在主线程ActivityThread中。Activity要展现出来的主视图是DecorView,它是一棵视图树。ViewRootImpl负责管理这个视图树和与WMS交互,与WMS交互通过WindowManagerImpl和WindowManagerGlobal。DecorView被包含在系统的通用窗口抽象类Window当中。视图对应的图形缓冲区由Surface管理。

1.2主要类介绍

其中涉及到的主要的类包括下面几个:

AMS(ActivityManagerService)

Activity的管理者。其实除了Activity,AMS也管Service等组件信息,另外AMS还管理Process信息。

Activity:描述一个Activity,它是与用户交互的基本单元。

ActivityRecord:描述单个Activity,Activity堆栈中的基本单元。

ActivityThread:每一个App进程有一个主线程,它由ActivityThread描述。它负责这个App进程中各个Activity的调度和执行,以及响应AMS的操作请求等。

ApplicationThread:AMS和Activity通过它进行通信。对于AMS而言,ApplicationThread代表了App的主线程。简而言之,它是AMS与ActivityThread进行交互的接口。注意ActivityThread和ApplicationThread之间的关系并不像Activity与Application。后者的关系是Application中包含了多个Activity,而前者ActivityThread和ApplicationThread是同一个东西的两种"View",ApplicationThread是在AMS眼中的ActivityThread。

ProcessRecord:描述一个App进程,包含了该进程中的Activity和Service列表。

TaskRecord:TaskRecord中的mActivities是ActivityRecord的列表,它们是按照历史顺序排序的。

ActivityStack:Activity堆栈,其中的ActivityRecord是通过TaskRecord这一层间接地被管理着。

ActivityStackSupervisor:ActivityStackSupervisor是ActivityStack的总管。4.4中默认引入了两个ActivityStack,一个叫Home stack,放Launcher和systemui,id为0;另一个是Applicationstack,放App的Activity,id可能是任意值。

ViewRootImpl:主要责任包括创建Surface,和WMS的交互和App端的UI布局和渲染。同时负责把一些事件发往Activity以便Activity可以截获事件。每一个添加到WMS中的窗口对应一个ViewRootImpl,通过WindowManagerGlobal向WMS添加窗口时创建。大多数情况下,它管理Activity顶层视图DecorView。总得来说,它相当于MVC模型中的Controller。

ViewRootImpl::W:用于向WMS提供接口,让WMS控制App端的窗口。它可看作是个代理,很多时候会调用ViewRootImpl中的功能。这种内嵌类的用法很多,特别是这种提供接口的代理类,如PhoneWindow::DecorView等。

Instrumentation:官方提供的Hook,主要用于测试。如果只关注窗口管理流程的话可以先无视。

WindowManagerImpl:Activity中与窗口管理系统通信的代理类,实现类是WindowManagerGlobal。WindowManagerGlobal是App中全局的窗口管理模块,因此是个Singleton。其中管理着该App中的ViewRootImpl,DecorView等结构。

WMS(WindowManagerService)

窗口的管理者。与AMS不同,一些高层的App中的概念,如进程等,WMS是不care的。因为WMS只对窗口进行管理,哪个进程的它不关心。像Activity这些概念在WMS仍然有,因为Activity对窗口的管理会产生影响。

WMS主要责任是维护窗口堆栈,计算每个窗口的layer信息交给SF,替App申请和调整绘图Surface,当窗口显示状态变化了还要通知其它模块,另外还要处理系统输入。所以说,WMS可能是与其它模块交互最多的模块之一了。它与AMS,App,SF及Input等模块都交集。Android中大体有以下几种窗口类型:1.应用窗口,一般来说就是Activity的主窗口。但也有些情况App没有Activity,直接把自定义的View添加到WMS中,比如浮动窗口。2.子窗口,需要有一个父窗口,如Context Menu,Option Menu,Popup Window和Dialog等。3.系统窗口,如状态栏,锁屏窗口,输入法窗口,壁纸窗口和Toast之流,由系统创建的,不依赖于父窗口。

Window:每个App虽然都可以做得各不相同,但是作为有大量用户交互的系统,窗口之间必须要有统一的交互模式,这样才能减小用户的学习成本。这些共性比如title, action bar的显示和通用按键的处理等等。Window类就抽象了这些共性。另外,它定义了一组Callback,Activity通过实现这些Callback被调用来处理事件。注意要和在WMS中的窗口区分开来,WMS中的窗口更像是App端的View。

PhoneWindow:PhoneWindow是Window类的唯一实现,至少目前是。这样的设计下如果要加其它平台的Window类型更加方便。每个Activity会有一个PhoneWindow,在attach到ActivityThread时创建,保存在mWindow成员中。

Context:运行上下文,Activity和Service本质上都是一个Context,Context包含了它们作为运行实体的共性,如启动Activity,绑定Service,处理Broadcast和Receiver等等。注意Application也会有Context。Activity的Context是对应Activity的,Activity被杀掉(比如转屏后)后就变了。所以要注意如果有生命周期很长的对象有对Activity的Context的引用的话,转屏、返回这种会引起Activity销毁的操作都会引起内存泄露。而Application的Context生命周期是和App进程一致的。关于Context的类结构图有下面的形式。Context是抽象类,定义了接口。ContextImpl是Context的实现类,包含了实现。而ContextWrapper是Context的包装类,它把请求delegate给其中的ContextImpl类去完成。ContextThemeWrapper是ContextWrapper的装饰类,它在ContextWrapper的基础上提供了自定义的主题。这结构初看有点乱,但结合下面的Decorator模式(java的设计模式)就一目了然了。

Surface:这是在App端管理图形缓冲区的类,其中最重要的是图形缓冲区队列。经由WMS从SF中得到IGraphicBufferProducer接口对象BufferQueue后,Surface便可以从该队列中queue和dequeue图形缓冲区。SurfaceControl在WMS中封装了Surface以及与SF的交互。Canvas和Surface从字面意思上很像,但前者其实更确切地说不是“画布”,而是“画家”。Surface中的图形缓冲区才是App的画布。

1.3结构流程分析

从结构上分析,应用启动大体可以分为3块:

1.ams启动的调用(见图一)

2.应用本身生命周期的调用(见图二)

3.Wms画图的调用(见图三)

简单的ams第一部分调用关系概括为:

图一

ActivityStackSupervisor

->startActivityMayWait

->startActivityLocked

->startActivityUncheckedLocked

->resumeTopActivitiesLocked

->resumeTopActivitiesLocked

->resumeTopActivityLocked(ActivityRecord prev) 

->resumeTopActivityLocked(ActivityRecordprev,Bundleoptions)

->startSpecificActivityLocked

->mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,"activity", r.intent.getComponent(), false, false, true);

它是从ams的startActivityAsUser开始,主要为应用创建各种对象信息,方便调用,比如ActivityInfo,ActivityRecord,Stack,TaskRecord对象,检查调用者权限,检查intent的合法性,为intent找到相应的应用,把原activity暂停放入后台task列表中,并且把ApplicationToken加入到wms的task队列里面。通知wms创建白色的过渡window,方法为mWindowManager.setAppStartingWindow。

第二部分应用生命周期的调用,主要从ActvityThread的handleLaunchActivity开始。

新启动的Activity的创建初始化主要是在handleLaunchActivity()中完成的。handleLaunchActivity()的作用是加载指定的Activity并运行。这其中在App端主要是创建ActivityThread,Activity,PhoneWindow,DecorView等对象,并调用Activity生命周期中的onCreate(),onResume()函数执行用户逻辑。正常点的App里onCreate里会调用setContentView设置主视图。setContentView()里主要是调用了installDecor(),其中会设置窗口的通用元素,如title, action bar之类,还会把xml文件inflate成布局对象。

简单的调用关系概括为:

handleLaunchActivity

->performLaunchActivity

 ->mInstrumentation.newActivity

-> activity.attach

->mInstrumentation.callActivityOnCreate 

 ->activity.onCreate

-> activity.performStart()

->handleResumeActivity

->wm.addView(decor, l);

图二

可以看到这时创建了DecorView,这便是Activity的主窗口的顶层视图。DecorView创建好后,handleResumeActivity()中会将它添加到WMS中去。当App向WMS添加窗口时,会调用WindowManagerImpl的addView()。注意WindowManagerImpl中的addView()函数和ViewGroup里的addView()函数完全不一样,后者是将View加入到本地的View组织架构中去,和WMS没有关系。

第三部分主要从addView()的流程如下:

首先通过WindowManagerImpl的addView()会创建对应的ViewRootImpl,然后ViewRootImpl申请WMS得到Session(如果是App第一个Activity会新建),其接口为IWindowSession,然后ViewRootImpl通过addToDisplay()把自己的ViewRootImpl::W(实现了IWindow接口)注册给WMS。addView()的工作主要包括创建ViewRootImpl,和远程WMS建立Session,并将当前视图注册到WMS这几步。可以看到App端通过WindowManagerGlobal调用addView(),调用链到WMS就变成addWindow(),概念发生了改变,这也印证上面提到的App端和WMS端的Window概念不一样的说法。

图三

2.Process流程

2.1进程启动

进程的启动的入口函数是AMS.startProcessLocked(String processName, ApplicationInfo info, boolean knownToBeDead, int intentFlags,String hostingType, ComponentName hostingName, boolean allowWhileBooting,boolean isolated, boolean keepIfLarge)。在前面的文章中知道,当启动Activity、Service、getContentProvider时如果目标进程未启动,那么就需要调用startProcessLocked()来启动目标进程。所有线程启动都是通过这里启动,包括activity,Service、Provider、Broadcast。

2.1.1 AMS.startProcessLocked()

final ProcessRecord startProcessLocked(){

......

    startProcessLocked(app, hostingType, hostingNameStr);

.......

}

private final void startProcessLocked(ProcessRecord app,String hostingType, String hostingNameStr) {

     // Start the process.  It will either succeed and return a result containing

    // the PID of the new process, or else throw a RuntimeException.

     Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",

    app.processName, uid, uid, gids, debugFlags, mountExternal,

    app.info.targetSdkVersion, app.info.seinfo,null);

}

1.调用Process.start("android.app.ActivityThread"来启动ActivityThread的线程,并在等待PROC_START_TIMEOUT时间后,查看线程时候启动超时。

2.start函数直接调用Process.startViaZygote()-->zygoteSendArgsAndGetResult(),进程是从Zygote中孵化出来的。

3.Zygote是向socket服务端写数据,把创建进程的请求通过socket通讯方式来让framework的进程孵化类zygote创建新进程。而数据就是argsForZygote——一个以字符串List形式的把Process.start()所有调用参数都包含在里面的变量。

socket服务端收到创建新进程的请求,ZygoteConnection.runOnce()接收到新进程的参数,然后调用Zygote.forkAndSpecialize()来fork一个子进程,在子进程中会接着关闭socket,调用ZygoteInit.invokeStaticMain(cloader, className, mainArgs),即调用ActivityThread.main()。新的应用进程会从ActivityThread的main()函数处开始执行。

2.1.2 ActivityThread.main()

startProcessLocked方法后面就调用了ActivityThread.main,启动ActivityThread.attach()

public static void main(String[] args) {

     Process.setArgV0("");

      Looper.prepareMainLooper();

     ActivityThread thread =newActivityThread();

     thread.attach(false);

}


private void attach(booleansystem) {

    sCurrentActivityThread=this;

    mSystemThread= system;

   if(!system) {

     RuntimeInit.setApplicationObject(mAppThread.asBinder());

      IActivityManager mgr = ActivityManagerNative.getDefault();

      mgr.attachApplication(mAppThread);

   }else{

  ......

}

2.1.3 AMS.attachApplicationLocked()

attachApplicationLocked()函数主要做一些新进程起来后的工作,比如设置ProcessRecord信息、获取ContentProvider信息返回给ActivityThread、启动该进程的Activity、Service、BroadCast等工作(四大组件相关),下面是详细分析。

1.在调用startProcessLocked()启动进程时,便将ProcessRecord和Pid加入mPidsSelfLocked中,故此时app不为null。

2.启动应用所包含的ContentProvider,相当于将ContentProvider注册到AMS中来,然后再把返回的参数providers传给ActivityThread,ActivityThread根据参数加载对应的ContentProvider到应用进程中来,应用进程加载ContentProvider完成后会调用AMS.publishContentProviders()将ContentProvider信息公开。(如果应用包含有provider的信息,被oom强制杀死时会自动重启线程)

3.保证应用的dex,只有在特殊的运行“没有预先dexopt”模式才有作用。

4.AMS侧对该进程进行一些初始化操作后边将一些必要信息传给ActivityThread,这个步骤对应用非常重要,应用很多初始化的数据都在这里生成,包括初始化时区,设置资源,density,dpi,cache目录信息,虚拟器大小等。

5.进程起来后,调用ActivityStackSupervisor.attachApplicationLocked来启动相应Activity。如果未启动的Activity要跑在当前启动的进程中,那么直接调用realStartActivityLocked()来启动当前,realStartActivityLocked就是启动Activity的真正函数。

6.进程起来后调用ActiveServices.attachApplicationLocked()启动相应服务,寻找要在当前进程中启动的Activity,并启动之。mPendingServices中保存着等待该进程启动的Service,现在进程已经启动了,所以mPendingServices中保存的服务也可以启动了,即在下面调用realStartServiceLocked()来启动服务。

7.处理广播,将原本要发到当前进程的广播处理掉,调用isPendingBroadcastProcessLocked()来判断当前启动的进程是否是PendingBroadcast等待的进程,如果是就把PendingBroadcast广播发送给当前这个进程中去。

上一篇下一篇

猜你喜欢

热点阅读