OpenHarmony应用启动过程

本文基于 OpenHarmony 源码梳理应用的启动过程,介绍 appspawn/ability_runtime/ace_engine/ets_runtime 等重要模块的初始化流程,以及它们之间的相互关系。

不同形态的 hap 应用在具体细节上会有一些差异,但整体的流程上是一致的。本文基于 OpenHarmoney 3.2 标准系统 FA 模式的 ets 应用进行阐述。

应用启动整体流程

查看各个进程的父子关系可知,OpenHarmony 的系统应用和用户应用进程,都是由应用孵化器(appspawn)拉起的。

应用启动的整理流程如下图所示:

说明:应用启动时,appspawn 进程会 fork 出一个应用子进程,创建 AceAbility 实现类和 AceContainer。

AceContainer 初始化过程中会在 JS 线程中创建 JS 运行环境,包括 JsEngine、NativeEngin、ArkJSRuntime、JSThread、EcmaVM 等重要组件。

启动流程详解

appspawn 创建应用进程:

应用日志:

08-05 17:58:11.955 255-255/appspawn I C02c11/APPSPAWN: [appspawn_service.c:408]child process com.example.myapplication success pid 2345

关键代码流程:

// base\startup\appspawn\standard\appspawn_service.cAPPSPAWN_STATIC void OnReceiveRequest(const TaskHandle taskHandle, const uint8_t *buffer, uint32_t buffLen)    AppSpawnProcessMsg(sandboxArg, &appProperty->pid);    // base/startup/appspawn/common/appspawn_server.c    int AppSpawnProcessMsg(AppSandboxArg *sandbox, pid_t *childPid)        if (client->cloneFlags & CLONE_NEWPID) {            pid = clone(AppSpawnChild, childStack + SANDBOX_STACK_SIZE, client->cloneFlags | SIGCHLD, (void *)sandbox);        pid = fork();  // fork出应用进程        *childPid = pid;        if (pid == 0) { // 子进程流程执行            AppSpawnChild((void *)sandbox);            int AppSpawnChild(void *arg)                struct AppSpawnContent_ *content = sandbox->content;                DoStartApp(content, client, content->longProcName, content->longProcNameLen);                    // notify success to father process and start app process                    NotifyResToParent(content, client, 0);                content->runChildProcessor(content, client); // 进入应用主线程 (ability_runtime 的 MainThread)        }

应用主线程初始化 Ability:

应用的整体状态流转是由 Ability 实例对象来控制完成的。因此应用进程拉起时,会先创建出 Ability。

不同的应用模型在这里会创建不同的实例类型:

// foundation\ability\ability_runtime\frameworks\native\ability\native\ability_impl_factory.cpp// AbilityImplFactory::MakeAbilityImplObject() 方法:switch (info->type) {        case AppExecFwk::AbilityType::PAGE:            if (info->isStageBasedModel) {                abilityImpl = std::make_shared<NewAbilityImpl>();            } else {                abilityImpl = std::make_shared<PageAbilityImpl>();            }            break;        case AppExecFwk::AbilityType::SERVICE:            abilityImpl = std::make_shared<ServiceAbilityImpl>();            break;        case AppExecFwk::AbilityType::DATA:            abilityImpl = std::make_shared<DataAbilityImpl>();            break;

AbilityImpl 实例创建后,应用开始进入 Start 状态,触发 AceAbility::OnStart() 回调。在该回调中,会创建 JS 运行环境。

③AceContainer 初始化

AceContainer 初始化可分为两个阶段:第一个阶段创建 JS 运行时环境(js_engine, native_engine, ets_runtime);第二个阶段调度 js_engine 开始读取 js 字节码文件(xxx.abc)。

阶段一:创建 JS 运行时环境

这里的代码流程比较长… 具体调用过程见上图说明。讲几个主要的点:

AceContianer 初始化时会创建一个任务执行线程 FlutterTaskExecutor,这就是后续 js 代码的执行线程。应用主线程把需要在js线程中执行的代码包装成 task,放到 FlutterTaskExecutor 中去执行。

创建 Js 引擎时可以选择不同的引擎类型,这是在源码编译阶段由宏开关控制的。

\foundation\arkui\ace_engine\frameworks\bridge\declarative_frontend\engine\declarative_engine_loader.cpp

RefPtr<JsEngine> DeclarativeEngineLoader::CreateJsEngine(int32_t instanceId) const{#ifdef USE_V8_ENGINE    return AceType::MakeRefPtr<V8DeclarativeEngine>(instanceId);#endif#ifdef USE_QUICKJS_ENGINE    return AceType::MakeRefPtr<QJSDeclarativeEngine>(instanceId);#endif#ifdef USE_ARK_ENGINE    return AceType::MakeRefPtr<JsiDeclarativeEngine>(instanceId);#endif}

宏开关在如下配置文件中定义:

foundation/arkui/ace_engine/adapter/ohos/build/config.gni

engine_defines = [ "USE_ARK_ENGINE" ]

③ArkNativeEngine 初始化时创建了 NAPI 层的各个重要组件(moduleManager, scopeManager, referenceManager, loop…)

ArkNativeEngine 向 js 运行环境中注册了一个"requireNapi()"方法,该方法是 js 应用 import 各种 NAPI 库的入口。

js 代码中的"import xxxx"在 hap 包编译阶段会改写为“requireNapi(xxx)”。

当这行代码被 js 引擎解释执行时,即会调用到 ArkNativeEngine 中注册的 requireNapi c++实现代码,通过 NAPI 的 ModuleManager 模块完成 xxxNAPI 模块 lib 库的加载。

阶段二:读取并执行 js 字节码文件

在 AceContainer::RunPage() 流程中,会依次创建两个 js 线程的 task,分别读取 app.abc 和 index.abc 文件。

细节 1:JsiDeclarativeEngine::LoadJs() 方法中是根据传入的 *.js 文件名去读取对应的 *.abc。

// foundation\arkui\ace_engine\frameworks\bridge\declarative_frontend\engine\jsi\jsi_declarative_engine.cppvoid JsiDeclarativeEngine::LoadJs(const std::string& url, const RefPtr<JsAcePage>& page, bool isMainPage)    ...    const char js_ext[] = ".js";    const char bin_ext[] = ".abc";    auto pos = url.rfind(js_ext);    std::string urlName = url.substr(0, pos) + bin_ext; // 将文件名 xxx.js 替换成 xxx.abc

细节 2:EcmaVM::InvokeEcmaEntrypoint() 方法中会执行 index.abc 中的入口函数 func_main_0,该函数在原始的 index.js 文件中并没有,是 hap 包编译后生成在 index.abc 文件中的。

写在最后

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙

  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习知识点,请移步前往小编:https://gitee.com/MNxiaona/733GH/blob/master/jianshu
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容