前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >OpenHarmony应用启动过程

OpenHarmony应用启动过程

原创
作者头像
小帅聊鸿蒙
发布2024-08-09 16:14:32
1290
发布2024-08-09 16:14:32
举报
文章被收录于专栏:鸿蒙开发笔记

本文基于 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 创建应用进程:

应用日志:

代码语言:shell
复制
08-05 17:58:11.955 255-255/appspawn I C02c11/APPSPAWN: [appspawn_service.c:408]child process com.example.myapplication success pid 2345

关键代码流程:

代码语言:shell
复制
// 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。

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

代码语言:shell
复制
// 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 引擎时可以选择不同的引擎类型,这是在源码编译阶段由宏开关控制的。

代码语言:shell
复制
\foundation\arkui\ace_engine\frameworks\bridge\declarative_frontend\engine\declarative_engine_loader.cpp

代码语言:shell
复制
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}

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

代码语言:shell
复制
foundation/arkui/ace_engine/adapter/ohos/build/config.gni

代码语言:shell
复制
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。

代码语言:shell
复制
// 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🚀,不定期分享原创知识;
  • 想要获取更多完整鸿蒙最新学习知识点,可关注B站:码牛课堂;

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 应用启动整体流程
  • 启动流程详解
    • ③AceContainer 初始化
      • AceContainer 初始化可分为两个阶段:第一个阶段创建 JS 运行时环境(js_engine, native_engine, ets_runtime);第二个阶段调度 js_engine 开始读取 js 字节码文件(xxx.abc)。
        • 阶段一:创建 JS 运行时环境
        • 阶段二:读取并执行 js 字节码文件
    • 写在最后
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档