【ITニュース解説】Node API Part-7 : Creating an ArkTS Runtime in Native Threads Using Node-API on HarmonyOSNext
2025年09月04日に「Dev.to」が公開したITニュース「Node API Part-7 : Creating an ArkTS Runtime in Native Threads Using Node-API on HarmonyOSNext」について初心者にもわかりやすいように丁寧に解説しています。
ITニュース概要
HarmonyOSNext上で、C++ネイティブコードからArkTSアプリロジックを動かす技術が紹介された。Node-APIを活用し、ネイティブスレッド内にArkTS実行環境を構築することで、C++の高性能とArkTSの柔軟性を兼ね備え、効率的なマルチスレッド処理を実現する。
ITニュース解説
HarmonyOS Nextという新しいオペレーティングシステムでは、アプリケーション開発において、高い性能が要求される部分にはC++のような「ネイティブコード」を使い、ユーザーインターフェース(UI)やアプリケーションの基本的なロジックには、TypeScriptを基にした「ArkTS」という言語を使うことが一般的だ。ネイティブコードはOSやハードウェアに近い低レベルの操作や、高速な処理が必要な場面で強みを発揮し、ArkTSは開発の柔軟性や生産性の高さが特徴である。両者の長所を組み合わせて、より高性能でリッチなアプリケーションを開発するためには、これらを連携させる技術が非常に重要になる。
しかし、ここには一つの課題がある。ArkTSのコードを実行するための「ランタイム環境」(プログラムを実行するための基盤)は、通常のネイティブスレッドの中では自動的に用意されない。そのため、C++のネイティブコードから直接ArkTSの関数を呼び出そうとしても、ランタイムが存在しないため実行できないのだ。特に、複数の処理を同時に進める「マルチスレッド」のシナリオでは、ネイティブスレッドの中でArkTSのロジックを実行したい場面が多く発生する。
この課題を解決し、C++のネイティブコードの性能とArkTSの柔軟性を融合させる強力な手段として、「Node-API」(NAPI)が提供されている。Node-APIは、C++などのネイティブコードと、JavaScript系のコード(ここではArkTS)の間で相互に機能を呼び出し、連携させるための標準的なインターフェースである。この記事では、このNode-APIを使って、ネイティブのC++スレッドの内部で手動でArkTSのランタイム環境を作り、その中でArkTSモジュールを読み込み、関数を実行する方法について解説する。
この連携を実現するための基本的な手順は次のようになる。まず、C++で作成した機能を、Node-APIを使ってArkTS側から呼び出せるように登録する。次に、ArkTSの処理を実行するために、C++の新しいスレッド(OSが提供するプログラムの実行単位)を生成する。このスレッドの中で、napi_create_ark_runtime()というNode-APIの関数を用いて、ArkTSのランタイム環境を初期化する。ランタイムが正常に準備できたら、napi_load_module_with_info()という関数を使って、実行したいArkTSのコードが含まれるモジュールを「動的にロード」(実行中にプログラムの一部を読み込むこと)する。モジュールが読み込まれた後には、napi_call_function()という関数を使って、そのモジュール内に定義されている特定のArkTS関数を呼び出すことができる。そして、すべての処理が終わった後には、napi_destroy_ark_runtime()でランタイム環境を適切に破棄し、使用したリソースを解放することが重要である。この一連のプロセスにより、ネイティブコードがマルチスレッド環境であっても、安全かつ効率的にArkTSのロジックを実行できるようになる。
具体的な実装例を見ていくと、まずindex.d.tsファイルでは、ArkTSコードがC++のネイティブ関数createArkRuntimeを呼び出すための「宣言」が行われている。これは、ArkTSコンパイラが、外部に存在するC++関数の名前や引数、戻り値の型を知るために必要な記述だ。
次に、この連携の中核をなすcreate_ark_runtime.cppファイルでは、C++のネイティブコードが記述されている。このファイルには、ArkTSランタイムの生成と管理、ArkTSモジュールのロード、そして関数の呼び出しを行う主要なロジックが含まれている。まず、CreateArkRuntimeFuncという関数が定義されており、これが新しいネイティブスレッドの中で実行される。この関数の中で、napi_create_ark_runtimeが呼び出されてArkTSのランタイム環境が作成される。もしランタイムの作成が成功したら、napi_load_module_with_infoを使って、指定されたパスとアプリケーションのバンドル名を持つArkTSモジュール(例ではObjectUtils)が読み込まれる。モジュールが正常にロードされると、そのモジュールからLoggerという名前の関数を取得し、napi_call_functionを使ってそのLogger関数を実行する。このLogger関数は、コンソールにメッセージを出力する役割を担っている。一連の処理が完了すると、napi_destroy_ark_runtimeでランタイム環境は破棄される。
このCreateArkRuntimeFuncは、CreateArkRuntimeという別のC++関数から呼び出される。CreateArkRuntime関数は、ArkTS側から呼び出されることを想定しており、新しいpthread(POSIXスレッド、OSが提供する標準的なスレッドの仕組み)を作成し、そのスレッドの中でCreateArkRuntimeFuncを実行する。pthread_joinを使うことで、このCreateArkRuntimeを呼び出したスレッドは、新しく作成されたスレッドの処理が完了するまで待機するようになっている。
create_ark_runtime.cppファイルの後半では、Init関数がNode-APIモジュールの初期化を担当している。この関数内で、createArkRuntimeという名前のプロパティとして、先ほどのCreateArkRuntime関数をArkTS側から利用できるように定義している。そして、napi_module構造体とnapi_module_register関数を使って、このC++コード全体をHarmonyOSが認識できる「ネイティブモジュール」として登録している。これにより、ArkTSコードからlibentry.soという共有ライブラリを通じて、C++で定義されたcreateArkRuntime関数を呼び出すことが可能になる。
Index.etsは、ArkTSで書かれたUIコードの例である。このコードでは、libentry.soという名前でネイティブモジュールをインポートし、画面上のテキスト要素をクリックすると、testNapi.createArkRuntime()という形で、C++で定義されたcreateArkRuntime関数が呼び出されるようになっている。これにより、ArkTSのUI操作がきっかけとなり、C++側で新しいスレッドが起動し、その中でArkTSのロジックが実行されるという一連の流れが実現される。
ObjectUtil.etsファイルは、C++コードから動的にロードされ、実行されるArkTS側の具体的なロジックを提供する。この例ではLoggerという単純な関数が定義されており、console.log("Console Log")とコンソールにメッセージを出力するだけだが、実際にはより複雑なビジネスロジックや処理をここに記述できる。
最後に、CMakeLists.txtは、C++コードをコンパイルし、ビルドするための設定ファイルである。このファイルで、create_ark_runtime.cppを共有ライブラリentryとしてビルドすることや、Node-API (libace_napi.z.so) やログ出力 (libhilog_ndk.z.so) といった必要なライブラリをリンクする設定が記述されている。
この手法を用いることには、多くの重要な利点がある。第一に、開発者はArkTSランタイムの「開始」と「終了」を細かく制御できるようになり、システムのリソースをより正確に管理できる。第二に、ArkTSランタイムを別のネイティブスレッドで起動するため、アプリケーションのメインスレッドがブロックされることなく、ArkTSのロジックを「非同期」に実行できる。これにより、ユーザーインターフェースの応答性を保ちながら、バックグラウンドで時間のかかる処理を実行することが可能になる。
さらに、C++のネイティブコードからArkTSモジュールを動的に呼び出せるため、ネイティブコードの優れたパフォーマンスと、TypeScriptをベースとしたArkTSの柔軟なロジックをシームレスに統合できる。このアプローチは、LoggerのようなArkTSモジュールを複数のネイティブスレッドや異なるコンテキストから再利用できるため、コードのモジュール化を促進し、開発の効率も向上させる。バックグラウンドでのログ記録、システムの監視、遠隔データ収集といったタスクを独立したArkTSランタイムにオフロードすることで、メインスレッドのパフォーマンスを大幅に改善することも可能だ。
また、ArkTSのモジュールを動的にロードして実行できるため、アプリケーションのロジックを柔軟に切り替えたり、プラグインのような拡張性の高いアーキテクチャを構築したりすることも可能になる。そして何よりも、napi_create_ark_runtimeやnapi_destroy_ark_runtimeといった公式のNode-API関数を使用することで、HarmonyOSのランタイムに関する制約やリソース制限(例えば、1つのプロセス内で同時に最大16個のランタイム環境しかサポートされない)に適合し、安全で互換性のある開発が可能になる。
結論として、HarmonyOS上でArkTSコードとネイティブC++コードを連携させることは、単に技術的に可能であるだけでなく、非常に強力な開発手法となる。Node-APIを用いてネイティブスレッド内に専用のArkTSランタイムを作成することで、開発者はArkTSのロジックがいつ、どのように実行されるかについて、より詳細な制御権を得られる。このアプローチは、パフォーマンスの最適化、バックグラウンドでの処理効率向上、そして動的なモジュール管理といった様々な可能性を切り開く。システムレベルのサービスを構築するにしても、高性能なアプリケーションを開発するにしても、このランタイム戦略を適用することで、HarmonyOSエコシステム内での効率的なスケーリングが実現できるだろう。ただし、1プロセスあたり16個のランタイム環境という制限があるため、設計時にはこの点に注意する必要がある。HarmonyOSのハイブリッドアーキテクチャを最大限に活用したいのであれば、この技術はぜひ習得すべきものとなる。