OpenHarmony_Ability启动流程分析
startAbility函数用于启动一个Ability,是AMS中提供的接口,并且在应用中可以通过Ability API调用。本文通过分析startAbility函数的执行过程,来介绍Ability启动时的主要流程。 总览 本文只涉及FA模型中的Page Ability与Stage模型中的Ability,不包含Particle Ability(FA)或Extension Ability(Stage
startAbility函数用于启动一个Ability,是AMS中提供的接口,并且在应用中可以通过Ability API调用。本文通过分析startAbility函数的执行过程,来介绍Ability启动时的主要流程。
总览
本文只涉及FA模型中的Page Ability与Stage模型中的Ability,不包含Particle Ability(FA)或Extension Ability(Stage)。
有关Ability的基本知识与对应的模型请参考:Ability框架概述
整体流程图:
这里我们将启动流程分为三个阶段:创建任务、启动应用(可选)、启动Ability,接下来深入细节看每个阶段的具体流程。
创建任务
整个启动流程最初就是创建任务(Mission)并将其加入到合适的任务链(MissionList)中,期间还会创建其他的对象比如AbilityRequest、AbilityRecord等。
1. 创建AbilityRequest
AbilityRequest用于描述一个启动请求,在AMS中创建。其中包含了待启动的Ability的信息、app的信息(通过BMS获取),请求的Want,调用者的信息等,以供后续流程获取这些信息。
2. 决策任务链
接下来需要决定目标任务放在哪个任务链中,任务链的选择是在MissionListManager中进行的,遵循着以下规则:
-
目标是launcher,使用launcherList
-
调用方是sa,new MissionList
-
调用方是launcher
-
如果目标是singleton,判断是否之前启动过该ability并找到对应的Mission和MissionList
-
不是则新建MissionList
-
-
如果调用方的MissionList不是defaultStandardList 与defaultSingleList,则使用调用方的MissionList
-
如果是,则新建MissionList,将调用方加入到该list中
2.1 MissionListManager
AMS维护了MissionListManager,每个user对应一个MissionListManager。MissionListManager用于管理所有的Mission(任务,一个任务对应一个AbilityRecord)与任务链MissionList,并提供了一些对ability的操作(最小化、结束、启动等),其内部维护了一个MissionList的列表。
2.2 MissionList
管理着用户启动的一个任务链,记录了任务之间的拉起关系,是启动的任务(Mission)的集合,使用链表进行管理。为了方便管理,有以下几个特殊的任务链:
-
launcherList:launcher单独占据一个MissionList
-
defaultSingleList:任务链被破坏后,singleton的任务被移动到该MissionList
-
defaultStandardList :任务链被破坏后,standard的任务被移动到该MissionList
2.3 Mission
Mission代表了用户启动的一个任务,一个Mission对应一个Ability记录。普通Ability每启动一次都会创建一个Mission,singleton类型的Ability则会复用。Mission仅用作记录信息。
3. 创建Misson与AbilityRecord
决定任务链后,需要创建Mission与其对应的AbilityRecord:
-
singleton模式的Ability尝试复用Mission
-
根据AbilityRequest创建AbilityRecord
-
创建InnerMissionInfo与MissionInfo
-
如果Mission为空,通过InnerMissionInfo的id、missionName与AbilityRecord生成Mission
-
AbilityRecord与Mission相互持有引用
-
将InnerMissionInfo存入MissionInfoMgr
3.1 任务类概览图
3.2 AbilityRequest与AbilityRecord
AbilityRequest表示一个请求,仅用作信息存储。AbilityRecord则表示一个Ability记录,AbilityRecord中的Ability信息与App信息均来自于AbilityRequest。AbilityRecord还存有want、preAbilityRecord、nextAbilityRecord、backAbilityRecord_、ability的运行信息等,以及一些对ability操作的api。后续AMS内通过AbilityRecord来获取Ability与其对应App的信息,并且对Ability的操作也是通过AbilityRecord来触发的。
3.3 InnerMissionInfo与MissionInfo
MissionInfo是元能力对外提供的用于描述任务信息的实体,其中包含任务的运行状态、运行时间、want引用、ability图标与名字等信息,对上提供任务api的数据就是来自于MissionInfo。InnerMissionInfo则是ams内部使用的实体,包含了MissionInfo的引用、bundleName,uid等信息。两者均为纯数据对象。
3.4 MissionInfoMgr
MissionInfoMgr管理着InnerMissionInfo,AMS与外部组件均直接或间接通过MissionInfoMgr来获取InnerMissionInfo中的MissionInfo,如当前任务的状态、快照等。WMS会为MissionInfoMgr注册快照处理器,用来生成任务快照。
4. 加入任务链
第一阶段的最后一步,是将创建的Mission加入到决策好的MissionList中,然后将MissionList添加至MissionListManager的顶部。
破坏任务链
在将Mission加入到MissionList中时,如果该Mission是被复用的,已经加入到MissionList中了,并且原来的任务链与现在的任务链不是同一个,或者调用方是Launcher或者sa,那么原任务链就会被破坏。原任务链内的standard任务被移动到该defaultStandardList ,singleton任务被移动到该defaultSingleList。
举个例子:
Ability A启动了singleton类型的Ability S,Ability B在后面某个时间点再一次启动了S,S原先的任务链是A的任务链,现在的任务链是B的任务链,两者不相同,A的任务链被破坏了。A的任务链中的任务会被移至default链中,S则被加入B的任务链顶部,B的任务链被移至MissionListManager顶部。
启动应用
任务与任务链准备完成后,通过AppMgrServiceInner::LoadAbility尝试装载Ability,其内部通过AppRunningManager判断应用是否已经启动,即是否是第一次启动。如果是会优先启动应用进程并创建Application,待应用启动完毕后再装载目标Ability。
1. 创建AppRunningRecord
AppRunningManager内部维护了所有应用的运行记录,即AppRunningRecord。AppRunningRecord记录了应用的信息、应用的运行状态(CREATE、READY、FOREGROUND、BACKGROUND、SUSPENDED、TERMINATED、END)、进程信息等,内部持有了模块运行信息列表。
应用第一次启动时,会先创建AppRunningRecord:
-
从BMS获取app、hap、ability的信息
-
决定进程名,规则为module.json中的process或bundleName
-
将以上信息传递给AppRunningManager来创建
-
为AppRunningRecord添加模块运行信息
2. 启动App进程
接下来根据进程名启动App进程:
-
如果在startAbility时,want内将coldStart设置为true,那么后续会执行冷启动流程
-
基于appspawn进程fork app进程
-
冷启动
-
使用execv执行子程序替换当前进程的正文、数据、堆和栈段
-
子程序为appspawn_start_app.cpp中的main函数
-
new一个AppSpawnServer,并调用AppColdStart函数
-
AppColdStart内会通过dlopen装载ace与nweb的so库
-
与热启动一样
-
-
热启动
-
配置应用沙箱
-
设置进程名
-
设置uid与gid
-
通知父进程启动结果
-
启动MainThread
-
3. 启动MainThread
接下来会调用AppExecFwk::MainThread::Start()函数,我们来看一看该函数内做了什么操作:
-
创建主EventRunner
-
创建MainThread实例
-
初始化与Main EventRunner对应的Main Handler
-
初始化一个名为TaskRunner的EventRunner与其对应的EventHandler,创建了名为TaskRunner的线程,主要用于Ability执行非UI任务
-
后续的所有流程包括启动应用、启动Ability均是在Start函数中触发的,在Start函数的最后是启动主EventRunner,这里将其提前以保持连贯性。EventRunner内部维护了一个消息队列,在启动后不断地从队列中取出事件交给对应的EventHandler处理。
4. 创建应用上下文
接下来程序执行到MainThread::Attach函数,最终会调用到MainThread的HandleLaunchApplication函数:
-
装载/system/lib/libace.z.so
-
创建ApplicationImpl对象
-
创建AbilityRecordMgr对象,用于管理AbilityLocalRecord
-
创建ContextDeal,并为其设置进程、应用、代码路径等信息,后续会将其设置到OHOSApplication对象中
-
创建OHOSApplication对象
-
初始化ResourceManager,用于应用加载资源
-
创建AbilityRuntime::ContextImpl,并将其设置到OHOSApplication对象中,作为AbilityStage Context的parent
-
Stage模式下会创建JsRuntime并设置给OHOSApplication,用于解释执行abc文件,其内部通过方舟引擎ArkNativeEngine实现该功能
4.1 Context概览图
4.2 ContextDeal与OHOSApplication
ContextDeal与OHOSApplication均继承自AppExecFwk::Context,区别就是OHOSApplication的直接父类是AppExecFwk::ApplicationContext。OHOSApplication中多了一些函数,如对AbilityStage的操作,以及Application的生命周期函数,如OnForeground、OnBackground。其还实现了AbilityLifecycleCallbacks,并提供了RegisterAbilityLifecycleCallbacks函数,用于在Ability生命周期变化时触发注册进来的回调函数。
ContextDeal与OHOSApplication相互持有,ContextDeal借助OHOSApplication实现CreateTaskDispatcher()系列的函数,OHOSApplication借助ContextDeal实现其他上下文函数,诸如GetProcessInfo、GetApplicationInfo、GetResourceManager等。
TaskDispatcher
TaskDispatcher与TaskExecutor一起协同工作,TaskDispatcher负责任务的分发,TaskExecutor则负责任务的执行。TaskDispatcher按业务目标分为很多类型,如UITaskDispatcher、主线程TaskDispatcher、ParallelTaskDispatcher、GlobalTaskDispatcher等。
4.3 ContextDeal与ContextImpl
ContextDeal是AppExecFwk::Context的实现,而ContextImpl是AbilityRuntime::Context的实现。两者有什么区别呢?
AppExecFwk::Context
-
程序执行框架中的上下文
-
包含了程序执行时需要的能力,如创建与获取TaskDispatcher、获取BMS、获取进程信息、对Mission的操作等
-
提供了大部分AbilityRuntime::Context的能力
AbilityRuntime::Context
-
Ability运行时的上下文
-
仅提供Ability运行中的信息获取,如获取应用信息、代码与缓存目录、资源管理器等。
4.4 OHOSApplication与ApplicationImpl
ApplicationImpl持有OHOSApplication,内部记录了Application的状态,以及一系列Perform函数,如PerformForeground、PerformBackground等,Perform函数内则会调用OHOSApplication中的生命周期函数。MainThread会在合适的时机调用Perform系列函数。
Application的状态
-
APP_STATE_CREATE
-
APP_STATE_READY
-
APP_STATE_FOREGROUND
-
APP_STATE_BACKGROUND
-
APP_STATE_TERMINATED
5. 启动应用
在Attach的最后,会调用ApplicationImpl::PerformAppReady来启动应用,也就是调用应用的生命周期函数并设置应用状态为APP_STATE_READY:
-
调用OHOSApplication的OnStart函数
-
应用状态设置为APP_STATE_READY
-
将AppRunningRecord中的应用状态也设置为APP_STATE_READY
-
启动Pending状态的Abilities,即状态为ABILITY_STATE_CREATE的Ability
启动Ability
在startAbility函数执行过程中,会根据Want对象中的BundleName与AbilityName创建AbilityRequest对象,最终在AppMgrServiceInner中创建AppRunningRecord对象。在创建时会为其添加ModuleRunningRecord,并且为ModuleRunningRecord添加AbilityRunningRecord。AbilityRunningRecord则是根据AbilityRecord中的AbilityInfo生成。
三者对应关系如下:
Application启动成功后,会根据AbilityRunningRecord启动Ability,即startAbility时指定的Ability
1. 创建AbilityLocalRecord
根据AbilityInfo与token生成AbilityLocalRecord,AbilityLocalRecord主要用于存储信息,如AbilityInfo、EventRunner、AbilityImpl、AbilityThread等。其被AbilityRecordMgr管理,可以通过其获取Ability相关信息。
2. 创建AbilityStage
接下来调用OHOSApplication::AddAbilityStage生成AbilityStage,该函数接受的参数是AbilityLocalRecord:
-
首先创建stageContext,类型为AbilityRuntime::ContextImpl
-
将stageContext的父context设置为OHOSApplication中的Application Context
-
寻找hap包内的AbilityStage.abc(ark运行时)并使用JsRuntime加载这个文件
-
如果hap内没有module.json(FA模型),会手动拼接AbilityStage.abc的路径为/assets/js/AbilityStage.abc,在加载该文件时会直接返回一个空的NativeReference对象
-
创建AbilityStage,Stage下具体实现类为JsAbilityStage,并将AbilityStage.abc解析出来的对象赋值给jsAbilityStageObj_
-
在Stage模型下创建js侧可用的AbilityStageContext对象(参考sdk目录下的api\application\AbilityStageContext.d.ts),FA模型由于jsAbilityStageObj_对象为空,不在此创建context
-
调用JS侧的OnCreate函数
-
将AbilityLocalRecord缓存至AbilityStage中
-
以moduleName为键缓存AbilityStage
AbilityStage作为Module级别的运行时,在Module加载与销毁的时候,可以通知开发者在此执行自己的逻辑,即生命周期。AbilityStage中记录了Module中的所有AbilityLocalRecord,OHOSApplication则记录了应用中所有的AbilityStage。
3. 创建AbilityThread
接着由MainThread创建AbilityThread,AbilityThread持有AbilityImpl,用于创建并管理调度ability的生命周期,一个ability对应一个AbilityThread:
-
创建AbilityThread对象并执行Attach函数
-
根据模型决定abilityName
-
FA:AceAbility
-
Stage:JsAbility
-
-
创建AbilityHandler,其继承自EventHandler,用于post调度Ability的任务。其会被设置到AbilityImpl与AbilityLocalRecord中
4. 创建Ability
-
通过AbilityLoader::GetInstance().GetAbilityByName(abilityName)创建Ability对象
-
AceAbility:通过REGISTER_AA(AceAbility)注册abilityName
-
REGISTER_AA则调用AbilityLoader::GetInstance().RegisterAbility注册abilityName,并返回new出来的Ability对象,即AceAbility
-
-
JsAbility:调用 Ability::Create函数,当前Runtime为JS类型则创建JsAbility
-
-
创建Ability的AppExecFwk::ContextDeal与AbilityRuntime::AbilityContextImpl,AbilityContextImpl中的Ability相关的函数如startAbility借由AbilityManagerClient实现,其他api借助AbilityRuntime::ContextImpl实现。具体参考4.1 Context概览图
5. 创建AbilityImpl
Ability创建完成后接着创建与其对应的AbilityImpl对象。AbilityImpl通过AbilityImplFactory创建,AbilityImpl持有Ability的引用,是Ability的包装类,会在合适的时机调用生命周期函数。不同模式实现类不同:
-
FA:PageAbilityImpl
-
Stage:NewAbilityImpl
6. Init Ability
接下来由AbilityImpl执行Ability初始化操作:
-
执行持有的Ability的Init,AceAbility与JsAbility都会先调用父类Ability的Init:
-
Ability
-
一些初始化工作,如lifecycle初始化、application、context等的赋值
-
如果不是stage模式会创建AbilityWindow,内部会创建WindowScene,用于与窗口的交互
-
-
AceAbility无额外的Init逻辑
-
JsAbility
-
通过JsRuntime解释执行ability文件,如MainAbility.abc
-
创建js侧可用的ability context对象(参考sdk目录下的api\application\AbilityContext.d.ts)
-
-
7. Forground Application
初始化Ability后执行attach AbilityThread,在此期间如果application的状态不是APP_STATE_FOREGROUND,则将application置入前台:
-
改变ApplicationImpl的状态为APP_STATE_FOREGROUND并执行OHOSApplication::OnForeground()
-
将待置入前台的ability置入前台
-
将application加入最近应用列表
8. Forground Ability
在将待置入前台的ability置入前台的过程中,根据ability的token找到对应的ModuleRunningRecord,并调用其OnAbilityStateChanged函数:
-
该函数会设置AbilityRunningRecord的状态为ABILITY_STATE_FOREGROUND,并回调OnAbilityRequestDone。
-
在AppScheduler::OnAbilityRequestDone的回调中,则通过MissionListManager找到对应的AbilityRecord,执行其ForegroundAbility函数.
-
最终调用链会执行到AbilityThread中AbilityImpl的HandleAbilityTransaction函数内,这里只关注Foreground的流程
8.1 Start
如果当前Ability的生命周期状态为AAFwk::ABILITY_STATE_INITIAL,优先执行start流程:
FA
-
调用AbilityImpl::Start(const Want &want)
-
调用内部持有的ability_的OnStart函数
-
FA对应的是AceAbility,内部会先调用Ability::OnStart(want)
-
根据应用的bundleName,决定window的类型
-
FA模型下会创建并初始化main window
-
更新context中的displayID、density等信息,并回调JS中的onUpdateConfiguration
-
更新resourceManager中的density、屏幕方向等信息
-
更新SystemProperties中的device info、color mode等信息
-
更新resourceManager中的locale信息
最后初始化AceContainer、创建FlutterAceView、执行js page的代码、调用js侧的onCreate
Stage
-
调用AbilityImpl::Start(const Want &want)
-
调用内部持有的ability_的OnStart函数
-
Stage对应的是JSAbility,内部会先调用Ability::OnStart(want);
-
同FA模型,区别是此时不会创建window
最后回调js中的onCreate
8.2 Forground
FA
在FA模型中,执行的是Active流程:
-
调用PageAbilityImpl::AbilityTransactionNew
-
该函数根据目标状态与当前状态执行不同的函数,在当前情况下会调用AbilityImpl::Active函数
-
调用内部持有的ability_的OnActive函数
-
FA对应的是AceAbility,内部会优先调用Ability::OnActive,继而执行abilityWindow_->OnPostAbilityActive(),最终会调用show来显示窗口
-
接着调用AceContainer::OnActive,最终回调js的onActive
-
将Ability状态设置为ABILITY_STATE_ACTIVE
Stage
-
调用NewAbilityImpl::AbilityTransaction函数
-
该函数根据目标状态与当前状态执行不同的函数,在当前情况下会调用AbilityImpl::Foreground函数
-
调用内部持有的ability_的OnForeground函数
-
Stage对应的是JSAbility,最终会调用到JsAbility::DoOnForeground
-
如果WindowScene未被创建则创建WindowScene、初始化main window并回调js中的onWindowStageCreate
-
调用main window的show函数,用于显示窗口
-
回调js中的onForeground
-
将Ability状态设置为ABILITY_STATE_FOREGROUND_NEW
到此Ability从启动到前台的流程就介绍完了。
更多推荐
所有评论(0)