积分584 / 贡献0

提问0答案被采纳0文章51

[经验分享] OpenHarmony_Ability启动流程分析 原创

Laval社区小助手 显示全部楼层 发表于 2023-12-26 15:19:49

详情:OpenHarmony_Ability启动流程分析

startAbility函数用于启动一个Ability,是AMS中提供的接口,并且在应用中可以通过Ability API调用。本文通过分析startAbility函数的执行过程,来介绍Ability启动时的主要流程。

总览

本文只涉及FA模型中的Page Ability与Stage模型中的Ability,不包含Particle Ability(FA)或Extension Ability(Stage)。

有关Ability的基本知识与对应的模型请参考:Ability框架概述

整体流程图: overview

这里我们将启动流程分为三个阶段:创建任务、启动应用(可选)、启动Ability,接下来深入细节看每个阶段的具体流程。

创建任务

整个启动流程最初就是创建任务(Mission)并将其加入到合适的任务链(MissionList)中,期间还会创建其他的对象比如AbilityRequest、AbilityRecord等。

1. 创建AbilityRequest

AbilityRequest用于描述一个启动请求,在AMS中创建。其中包含了待启动的Ability的信息、app的信息(通过BMS获取),请求的Want,调用者的信息等,以供后续流程获取这些信息。

2. 决策任务链

接下来需要决定目标任务放在哪个任务链中,任务链的选择是在MissionListManager中进行的,遵循着以下规则:

  1. 目标是launcher,使用launcherList
  2. 调用方是sa,new MissionList
  3. 调用方是launcher
    • 如果目标是singleton,判断是否之前启动过该ability并找到对应的Mission和MissionList
    • 不是则新建MissionList
  4. 如果调用方的MissionList不是defaultStandardList 与defaultSingleList,则使用调用方的MissionList
  5. 如果是,则新建MissionList,将调用方加入到该list中

2.1 MissionListManager

AMS维护了MissionListManager,每个user对应一个MissionListManager。MissionListManager用于管理所有的Mission(任务,一个任务对应一个AbilityRecord)与任务链MissionList,并提供了一些对ability的操作(最小化、结束、启动等),其内部维护了一个MissionList的列表。

2.2 MissionList

管理着用户启动的一个任务链,记录了任务之间的拉起关系,是启动的任务(Mission)的集合,使用链表进行管理。为了方便管理,有以下几个特殊的任务链:

  1. launcherList:launcher单独占据一个MissionList
  2. defaultSingleList:任务链被破坏后,singleton的任务被移动到该MissionList
  3. defaultStandardList :任务链被破坏后,standard的任务被移动到该MissionList

2.3 Mission

Mission代表了用户启动的一个任务,一个Mission对应一个Ability记录。普通Ability每启动一次都会创建一个Mission,singleton类型的Ability则会复用。Mission仅用作记录信息。

3. 创建Misson与AbilityRecord

决定任务链后,需要创建Mission与其对应的AbilityRecord:

  1. singleton模式的Ability尝试复用Mission
  2. 根据AbilityRequest创建AbilityRecord
  3. 创建InnerMissionInfo与MissionInfo
  4. 如果Mission为空,通过InnerMissionInfo的id、missionName与AbilityRecord生成Mission
  5. AbilityRecord与Mission相互持有引用
  6. 将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:

  1. 从BMS获取app、hap、ability的信息
  2. 决定进程名,规则为module.json中的process或bundleName
  3. 将以上信息传递给AppRunningManager来创建
  4. 为AppRunningRecord添加模块运行信息

2. 启动App进程

接下来根据进程名启动App进程:

  1. 如果在startAbility时,want内将coldStart设置为true,那么后续会执行冷启动流程
  2. 基于appspawn进程fork app进程
  3. 冷启动
    • 使用execv执行子程序替换当前进程的正文、数据、堆和栈段
    • 子程序为appspawn_start_app.cpp中的main函数
    • new一个AppSpawnServer,并调用AppColdStart函数
    • AppColdStart内会通过dlopen装载ace与nweb的so库
    • 与热启动一样
  4. 热启动
    • 配置应用沙箱
    • 设置进程名
    • 设置uid与gid
    • 通知父进程启动结果
    • 启动MainThread

3. 启动MainThread

接下来会调用AppExecFwk::MainThread::Start()函数,我们来看一看该函数内做了什么操作:

  1. 创建主EventRunner
  2. 创建MainThread实例
  3. 初始化与Main EventRunner对应的Main Handler
  4. 初始化一个名为TaskRunner的EventRunner与其对应的EventHandler,创建了名为TaskRunner的线程,主要用于Ability执行非UI任务
  5. 后续的所有流程包括启动应用、启动Ability均是在Start函数中触发的,在Start函数的最后是启动主EventRunner,这里将其提前以保持连贯性。EventRunner内部维护了一个消息队列,在启动后不断地从队列中取出事件交给对应的EventHandler处理。

4. 创建应用上下文

接下来程序执行到MainThread::Attach函数,最终会调用到MainThread的HandleLaunchApplication函数:

  1. 装载/system/lib/libace.z.so
  2. 创建ApplicationImpl对象
  3. 创建AbilityRecordMgr对象,用于管理AbilityLocalRecord
  4. 创建ContextDeal,并为其设置进程、应用、代码路径等信息,后续会将其设置到OHOSApplication对象中
  5. 创建OHOSApplication对象
  6. 初始化ResourceManager,用于应用加载资源
  7. 创建AbilityRuntime::ContextImpl,并将其设置到OHOSApplication对象中,作为AbilityStage Context的parent
  8. 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:

  1. 调用OHOSApplication的OnStart函数
  2. 应用状态设置为APP_STATE_READY
  3. 将AppRunningRecord中的应用状态也设置为APP_STATE_READY
  4. 启动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:

  1. 首先创建stageContext,类型为AbilityRuntime::ContextImpl
  2. 将stageContext的父context设置为OHOSApplication中的Application Context
  3. 寻找hap包内的AbilityStage.abc(ark运行时)并使用JsRuntime加载这个文件
  4. 如果hap内没有module.json(FA模型),会手动拼接AbilityStage.abc的路径为/assets/js/AbilityStage.abc,在加载该文件时会直接返回一个空的NativeReference对象
  5. 创建AbilityStage,Stage下具体实现类为JsAbilityStage,并将AbilityStage.abc解析出来的对象赋值给jsAbilityStageObj_
  6. 在Stage模型下创建js侧可用的AbilityStageContext对象(参考sdk目录下的api\application\AbilityStageContext.d.ts),FA模型由于jsAbilityStageObj_对象为空,不在此创建context
  7. 调用JS侧的OnCreate函数
  8. 将AbilityLocalRecord缓存至AbilityStage中
  9. 以moduleName为键缓存AbilityStage

AbilityStage作为Module级别的运行时,在Module加载与销毁的时候,可以通知开发者在此执行自己的逻辑,即生命周期。AbilityStage中记录了Module中的所有AbilityLocalRecord,OHOSApplication则记录了应用中所有的AbilityStage。

3. 创建AbilityThread

接着由MainThread创建AbilityThread,AbilityThread持有AbilityImpl,用于创建并管理调度ability的生命周期,一个ability对应一个AbilityThread:

  1. 创建AbilityThread对象并执行Attach函数
  2. 根据模型决定abilityName
    • FA:AceAbility
    • Stage:JsAbility
  3. 创建AbilityHandler,其继承自EventHandler,用于post调度Ability的任务。其会被设置到AbilityImpl与AbilityLocalRecord中

4. 创建Ability

  1. 通过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
  2. 创建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初始化操作:

  1. 执行持有的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置入前台:

  1. 改变ApplicationImpl的状态为APP_STATE_FOREGROUND并执行OHOSApplication::OnForeground()
  2. 将待置入前台的ability置入前台
  3. 将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
  1. 调用AbilityImpl::Start(const Want &want)
  2. 调用内部持有的ability_的OnStart函数
  3. FA对应的是AceAbility,内部会先调用Ability::OnStart(want)
  4. 根据应用的bundleName,决定window的类型
  5. FA模型下会创建并初始化main window
  6. 更新context中的displayID、density等信息,并回调JS中的onUpdateConfiguration
  7. 更新resourceManager中的density、屏幕方向等信息
  8. 更新SystemProperties中的device info、color mode等信息
  9. 更新resourceManager中的locale信息

最后初始化AceContainer、创建FlutterAceView、执行js page的代码、调用js侧的onCreate

Stage
  1. 调用AbilityImpl::Start(const Want &want)
  2. 调用内部持有的ability_的OnStart函数
  3. Stage对应的是JSAbility,内部会先调用Ability::OnStart(want);
  4. 同FA模型,区别是此时不会创建window

最后回调js中的onCreate

8.2 Forground

FA

在FA模型中,执行的是Active流程:

  1. 调用PageAbilityImpl::AbilityTransactionNew
  2. 该函数根据目标状态与当前状态执行不同的函数,在当前情况下会调用AbilityImpl::Active函数
  3. 调用内部持有的ability_的OnActive函数
  4. FA对应的是AceAbility,内部会优先调用Ability::OnActive,继而执行abilityWindow_->OnPostAbilityActive(),最终会调用show来显示窗口
  5. 接着调用AceContainer::OnActive,最终回调js的onActive
  6. 将Ability状态设置为ABILITY_STATE_ACTIVE
Stage
  1. 调用NewAbilityImpl::AbilityTransaction函数
  2. 该函数根据目标状态与当前状态执行不同的函数,在当前情况下会调用AbilityImpl::Foreground函数
  3. 调用内部持有的ability_的OnForeground函数
  4. Stage对应的是JSAbility,最终会调用到JsAbility::DoOnForeground
  5. 如果WindowScene未被创建则创建WindowScene、初始化main window并回调js中的onWindowStageCreate
  6. 调用main window的show函数,用于显示窗口
  7. 回调js中的onForeground
  8. 将Ability状态设置为ABILITY_STATE_FOREGROUND_NEW

到此Ability从启动到前台的流程就介绍完了。

无用

©著作权归作者所有,转载或内容合作请联系作者

您尚未登录,无法参与评论,登录后可以:
参与开源共建问题交流
认同或收藏高质量问答
获取积分成为开源共建先驱

Copyright   ©2023  OpenHarmony开发者论坛  京ICP备2020036654号-3 |技术支持 Discuz!

返回顶部