This article examines the application launch sequence in OpenHarmony based on its source code, detailing the initialization procedures of key components such as process_spawner/ability_runtime/ui_engine/ets_runtime and their interconnections.
While different types of HAP applications may have specific variations, the overall launch flow remains consistent. This explanation focuses on ETS applications using the FA model in OpenHarmony 3.2 standard system.
Application Launch Overview
Examining the parent-child relationships between processes reveals that both system and user applications in OpenHarmony are initiated by the process spawner.
The application launch process is illustrated below:
Note: During application startup, the process_spawner creates a child process, implementing AceAbility and AceContainer classes.
During AceContainer initialization, a JavaScript execution environment is established in the JS thread, including essential components like JsEngine, NativeEngine, ArkJSRuntime, JSThread, and EcmaVM.
Detailed Launch Process
①Process Spawner Application Creation:
Application logs:
08-05 17:58:11.955 255-255/process_spawner I C02c11/PROCESS_SPAWNER: [process_spawner_service.c:408]child process com.example.myapplication success pid 2345
Key code flow:
// base\startup\process_spawner\standard\process_spawner_service.c
PROCESS_SPAWNER_STATIC void OnReceiveRequest(const TaskHandle taskHandle, const uint8_t *buffer, uint32_t buffLen)
ProcessSpawnProcessMsg(sandboxArg, &appProperty->pid);
// base/startup/process_spawner/common/process_spawner_server.c
int ProcessSpawnProcessMsg(AppSandboxArg *sandbox, pid_t *childPid)
if (client->cloneFlags & CLONE_NEWPID) {
pid = clone(ProcessSpawnChild, childStack + SANDBOX_STACK_SIZE, client->cloneFlags | SIGCHLD, (void *)sandbox);
pid = fork(); // Create application process
*childPid = pid;
if (pid == 0) { // Child process execution
ProcessSpawnChild((void *)sandbox);
int ProcessSpawnChild(void *arg)
struct ProcessSpawnContent_ *content = sandbox->content;
DoStartApp(content, client, content->longProcName, content->longProcNameLen);
// Notify success to parent process and start app process
NotifyResToParent(content, client, 0);
content->runChildProcessor(content, client); // Enter application main thread (ability_runtime's MainThread)
②Application Main Thread Initialization:
The overall state transition of an application is controlled by its Ability instance. Therefore, when an application process is launched, an Ability is first created.
Different application models create different enstance types:
// foundation\ability\ability_runtime\frameworks\native\ability\native\ability_impl_factory.cpp
// AbilityImplFactory::CreateAbilityImplObject() method:
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;
After creating the AbilityImpl instance, the application enters the Start state, triggering the AceAbility::OnStart() callback. In this callback, the JavaScript execution environment is created.
③AceContainer Initialization
AceContainer initialization occurs in two phases: first creating the JavaScript runtime environment (js_engine, native_engine, ets_runtime); second, instructing js_engine to begin reading JavaScript bytecode files (xxx.abc).
Phase One: JavaScript Runtime Environment Creation
The code flow here is extensive... The specific call process is illustrated in the diagram above. Key points include:
①AceContainer initialization creates a task execution thread named FlutterTaskExecutor, which becomes the execution thread for subsequent JavaScript code. The application main thread wraps code that needs to run in the JS thread into tasks and places them in FlutterTaskExecutor for execution.
②When creating the JsEngine, different engine types can be selected, controlled by macro switches during the source code compilation phase.
foundation\arkui\ui_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
}
Macro switches are defined in the folllowing configuration file:
foundation/arkui/ui_engine/adapter/ohos/build/config.gni
engine_defines = [ "USE_ARK_ENGINE" ]
③ArkNativeEngine initialization creates important NAPI layer components (moduleManager, scopeManager, referenceManager, loop...)
④ArkNativeEngine registers a "requireNapi()" method in the JavaScript runtime environment. This method serves as the entry point for JavaScript applications to import various NAPI libraries.
JavaScript code with "import xxxx" is rewritten to "requireNapi(xxx)" during the HAP package compilation phase. When this code is interpreted and executed by the JavaScript engine, it calls the requireNapi C++ implementation registered in ArkNativeEngine, completing the loading of the xxxNAPI module library through NAPI's ModuleManager.
Phase Two: Reading and Executing JavaScript Bytecode Files
In the AceContainer::RunPage() process, two JavaScript thread tasks are created sequentially to read app.abc and index.abc files.
Detail 1: In JsiDeclarativeEngine::LoadJs(), the method reads the corresponding *.abc file based on the input *.js filename.
// foundation\arkui\ui_engine\frameworks\bridge\declarative_frontend\engine\jsi\jsi_declarative_engine.cpp
void 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; // Replace filename xxx.js with xxx.abc
Detail 2: In EcmaVM::ExecuteEcmaEntrypoint(), the entry function func_main_0 in index.abc is executed. This function doesn't exist in the original index.js file but is generated in the index.abc file during HAP package compilation.