One-to-many service discovery and dynamic loading method and system in a harmonious application
By using the @HMModularService decorator and AST analysis to generate mapping tables in HarmonyOS applications, and combining this with HarmonyOS native interfaces to dynamically load services, the problem of one-to-many service discovery and dynamic loading in HarmonyOS applications is solved, achieving module decoupling and flexible service management.
Patent Information
- Application Number
- CN202610793219.4
- Authority / Receiving Office
- CN · China
- Patent Type
- Applications(China)
- Current Assignee / Owner
- Filing Date
- 2026-06-03
- Publication Date
- 2026-08-25
AI Technical Summary
HarmonyOS applications lack native SPI support, which cannot effectively solve one-to-many service scenarios, cannot eliminate compile-time dependencies, has poor runtime flexibility, and cannot dynamically discover and load new service implementations.
By using the @HMModularService decorator to mark service classes in the ArkTS/ETS source code, module-level and global mapping tables are generated. Services are dynamically loaded using the napi_load_module_with_info interface of the HarmonyOS ArkTS runtime. Combined with AST analysis and annotation processing, service metadata is automatically scanned, supporting one-to-many service discovery and dynamic loading.
It achieves decoupled communication between modules, supports parallel development by multiple teams, automatically registers services without manual configuration, loads services on demand at runtime, supports flexible lifecycle management of service instances, and ensures deterministic loading order.
Smart Images

Figure CN122633270A_ABST
Abstract
Description
Technical Field
[0001] This invention relates to the field of mobile application software development, specifically to a method and system for one-to-many service discovery and dynamic loading in HarmonyOS applications. More particularly, it relates to a method and system for SPI (Service Provider Interface) service discovery and dynamic loading based on a mapping table in HarmonyOS applications. Background Technology
[0002] In large-scale mobile application development, modular architecture has become the mainstream trend. Applications are typically broken down into multiple functional modules (such as homepage, market data, trading, news, etc.), each developed, compiled, and tested independently. Modules need to communicate through well-defined interfaces, and traditional cross-module communication methods mainly include the following: (1) Hard-coded direct reference method: Directly referencing specific implementation classes between modules. This method leads to strong coupling between modules, increases compilation dependencies, reduces development efficiency, and makes it impossible to replace the implementation at runtime.
[0003] (2) Java SPI mechanism: In the Java ecosystem, the ServiceLoader searches for the implementation class of the interface in the configuration file under the META-INF / services / directory. However, HarmonyOS applications are developed based on the ArkTS / ETS language and compiled using the Ark Compiler. Its runtime does not support the Java ServiceLoader mechanism, nor can it directly use Java's reflection mechanism for dynamic class loading.
[0004] (3) Routing framework approach: Cross-module navigation and communication are achieved through URL routing, such as ARouter. This type of solution is mainly used for page navigation, has limited support for service interface level calls, and cannot handle scenarios where one interface corresponds to multiple implementations (i.e., one-to-many relationship) well.
[0005] (4) HarmonyOS native dynamic loading: HarmonyOS provides native APIs such as napi_load_module_with_info to support the dynamic loading of modules. However, this API is at a low level and requires developers to manually manage module paths, instantiation and lifecycle. It lacks a set of upper-level service discovery and interface mapping mechanisms.
[0006] Deficiencies of existing technology: The lack of native SPI support on the HarmonyOS platform is a significant drawback: HarmonyOS applications use the ArkTS language and the Ark Compiler, which are incompatible with the Java ecosystem, making it impossible to use the Java SPI mechanism directly. Furthermore, the HarmonyOS system itself does not provide a high-level service discovery framework.
[0007] Insufficient support for one-to-many scenarios: Traditional solutions mainly address one-to-one API calls, meaning an API has only one implementation. However, in real-world business scenarios, an API often needs to have different implementations in multiple modules (e.g., the "application startup callback" API, where modules like the homepage, market data, and trading all need to register their own startup logic). Existing solutions lack native support for this one-to-many relationship.
[0008] Compile-time dependencies cannot be eliminated: Direct references require the implementation class to be determined at compile time, making it impossible to truly decouple modules.
[0009] Poor runtime flexibility: It cannot dynamically discover and load new service implementations at runtime, nor does it support filtering specific service implementations by conditions (such as filtering by identifier).
[0010] Patent application CN121411953A discloses a HarmonyOS system adaptation method based on a hybrid architecture that supports dynamic expansion. Its key technical points include: a registration verification step: when an application module registers on a hardware device node, the application module's configuration file is verified using a preset multi-dimensional verification mechanism to obtain the registration verification result; a runtime monitoring step: during application module operation, resource usage is collected in real time using an improved set of monitoring indicators, and multi-dimensional monitoring data with time stamps is generated; a resource demand prediction step: multi-dimensional monitoring data is acquired, and a prediction result is generated based on a time-series prediction model, reflecting the application module's resource demand in the future; and a dynamic resource adjustment step: based on the prediction result and the registration verification result, the resource allocation of the application module is dynamically adjusted using a preset dynamic resource adjustment strategy. However, this patent cannot completely solve the existing technical problems, nor can it meet the needs of this invention. Summary of the Invention
[0011] To address the shortcomings of existing technologies, the purpose of this invention is to provide a method and system for one-to-many service discovery and dynamic loading in HarmonyOS applications.
[0012] The method for one-to-many service discovery and dynamic loading in HarmonyOS applications provided by the present invention includes: Step 1: During the project compilation and build phase, the ArkTS / ETS source code is analyzed using the TypeScript compiler API. All class declaration nodes marked with the @HMModularService decorator are identified. The service path, class name, module name, instantiation mode, and unique identifier of these class declaration nodes are extracted as metadata. A module-level mapping table file is generated for each module, and all module-level mapping table files are then merged into a global mapping table file. The global mapping table file is stored in key-value pairs, where the key is the service path and the value is an array of metadata for all implementations corresponding to that service path. Step 2: When the application starts, the global mapping table file is read and parsed into a mapping data structure in memory in the HarmonyOS ArkTS runtime environment; the mapping data structure is a Map data structure with service paths as keys and implementation item arrays as values; Step 3: Receive the service query request from the caller, and search for all corresponding implementation items in the memory-mapped data structure according to the service path in the request; when the caller provides filtering conditions, filter the result set according to the metadata of the implementation items, and return a subset of implementation items that meet the conditions; Step 4: Based on the module path and class name recorded in the returned implementation item, call the native interface napi_load_module_with_info unique to HarmonyOS ArkTS runtime to dynamically load the target HSP module, obtain the constructor of the target class from the module's exported object, and create an instance object through the constructor.
[0013] Preferably, the specific rules for AST analysis are as follows: After creating an abstract syntax tree using the source file as the analysis unit, the class declaration nodes are recursively traversed, and only class declarations with the @HMModularService service declaration decorator are considered as candidate service implementation classes; for candidate classes, the object literal parameter in the decorator call expression is read, and the path, singleton, and id fields are extracted, while the class name and source file path of the class declaration node are read; if the decorator parameter is a string, boolean value, or numeric literal, its value is directly recorded; if the decorator parameter references a constant or static member of an object, the source file where the reference is located is parsed according to the import declaration, and the corresponding constant value is further parsed in the referenced source file; if the same class is decorated by multiple decorators, only the decorator whose name matches @HMModularService is processed; if multiple @HMModularService decorators appear on the same class, conflict verification is performed according to the preset rules during the build phase, and duplicate service declarations are prompted.
[0014] Preferably, the path in @HMModularService is used to identify the service interface path. The compile-time mapping table generation module generates two different fields when scanning decorators: servicePath is used as the index key for service discovery, and moduleLoadPath is used as the path for dynamically loading target modules; moduleLoadPath is obtained by normalizing the source file path, the name of the module to which it belongs, the module type, and the name of the exported class. The path conversion rules include: First, identifying the module name and module type of the source file based on its project directory. Module types include entry, feature, har, and hsp. Second, removing the src / main / ets prefix, file extension, and local project absolute path from the source code relative path, converting it into a module relative path that ArkTS can recognize at runtime. Third, concatenating different loading prefixes based on the module type: for modules within the same HAP, using the entry module context and relative path; for HSP dynamic shared packages, using the HSP module name, package name, and module export path to form the loading description information. Finally, writing bundleName, entryModuleName, moduleName, moduleLoadPath, and className together into the HMMItem.
[0015] Preferably, the process of calling the native interface `napi_load_module_with_info` includes: converting the module relative path in the implementation item into a module path recognizable by the ArkTS runtime, and combining the application package name and entry module name information into module context information; passing the module path and module context information as string parameters to the NAPI method through the Native wrapper layer; locating the target ArkTS module file within the current application package and its HAP / HSP modules based on the module path and module context information, and handing it over to the ArkTS runtime to perform module resolution, dependency resolution, and module initialization; loading the compilation artifacts corresponding to the module, establishing the module execution context, and generating the exported object of the module; and returning the exported object to the ArkTS side through the Native wrapper layer.
[0016] Preferably, a module export object cache, ModuleExportCache, is set up, with a cache key consisting of a combination of bundleName, moduleName, and moduleLoadPath. If the same module path has already been successfully loaded, subsequent service instantiations directly reuse the cached export object without repeatedly calling the underlying loading interface. A separate ServiceInstanceCache is set up, with an instance cache key consisting of a combination of servicePath, id, moduleName, and className. Instances are cached only for implementations where singleton=true. Implementations where singleton=false are not cached, and a new instance is created for each query.
[0017] Preferably, the dependencies, loading priorities, and enabling status of each module are managed through a global configuration file hmm_global_config.json. The global configuration file defines the dependency list, loading priority value, and enabling flag for each module. During the mapping table loading phase, the modules are sorted according to their priority values, and the service implementations of each module are loaded in descending order of priority. Before loading a module, it is checked whether its dependent modules have been loaded.
[0018] Preferably, a loading event listener interface is provided, and the caller registers a listener to receive the service loading success event onLoaded and the service loading failure event onModuleLoadFailed; when the service is successfully instantiated, a success callback is triggered to pass implementation information; when a module is not found, a class is not found, or an instantiation exception occurs during dynamic loading, a failure callback is triggered to pass implementation information and error details.
[0019] The one-to-many service discovery and dynamic loading system in HarmonyOS applications provided by the present invention includes: Service annotation module: Used to provide compile-time service declaration decorators. By adding the @HMModularService decorator to the ArkTS class declaration, the class is marked as an implementation of a certain service interface; the decorator parameters include service path, singleton instantiation mode, and unique identifier id; The compile-time mapping table generation module is used during the project compilation and build phase to perform AST syntax tree analysis on the ArkTS / ETS source code through the TypeScript compiler API, identify all class declaration nodes marked with the @HMModularService decorator, extract their metadata, first generate a module-level mapping table file for each module, and then merge all module-level mapping tables into a global mapping table file; the global mapping table file is stored in key-value pair format, where the key is the service path and the value is an array of metadata of all implementations corresponding to that service path; Mapping table loading and parsing module: used during application startup, to read the global mapping table file generated during compilation and parse it into a mapping data structure in memory in the HarmonyOS ArkTS runtime environment; the mapping table loading and parsing module converts the JSON format mapping table into a Map data structure with service path as key and implementation item array as value; Service discovery and filtering module: It is used to receive service query requests from callers, search for all corresponding implementation items in the memory-mapped data structure according to the service path in the request; when the caller provides filtering conditions, it filters the result set according to the metadata of the implementation items and returns a subset of implementation items that meet the conditions. Dynamic loading and instantiation module: This module receives the implementation items returned by the service discovery module. Based on the module path and class name recorded in the implementation item, it calls the native `napi_load_module_with_info` interface unique to the HarmonyOS ArkTS runtime to dynamically load the target HSP module. It obtains the constructor of the target class from the module's exported object and creates an instance object through the constructor. For implementation items marked as singletons, the instance is cached after the first creation, and subsequent requests directly return the cached instance. For non-singleton implementation items, a new instance is created for each request.
[0020] Preferably, in the compile-time mapping table generation module, the specific rules for AST analysis are as follows: after creating an abstract syntax tree with the source file as the analysis unit, the class declaration nodes are recursively traversed, and only class declarations with the @HMModularService service declaration decorator are used as candidate service implementation classes; for candidate classes, the object literal parameter in the decorator call expression is read, and the path, singleton, and id fields are extracted, while the class name and source file path of the class declaration node are read; for inheritance, interface implementation, and generic relationships, the compile-time mapping table generation module performs registration judgment based on whether the current class is marked by the service declaration decorator, without relying on runtime reflection information of the parent class, interface, or generic parameters.
[0021] Preferably, the dynamic loading and instantiation module further includes a logical unloading mechanism: when a module is configured to be disabled or the application enters a stage where resources need to be released, the system first stops service discovery for the module's implementation, then removes the service instance corresponding to the module from the ServiceInstanceCache, and calls the optional dispose, onUnload, or equivalent release interface of the implementation; at the same time, it clears the query result cache related to the module.
[0022] Compared with the prior art, the present invention has the following beneficial effects: (1) Each functional module of the present invention depends only on the service interface definition (HMModularKitCore) and does not depend on the specific implementation class; the modules communicate decoupled through the service path, and any implementation change of any module does not affect the compilation and operation of other modules; this makes it possible for multiple teams to develop in parallel, and each module can be compiled and tested independently.
[0023] (2) The global mapping table of this invention uses the service path as the key and the implementation item array as the value, which naturally supports a service interface having different implementations in multiple modules. The caller can obtain all implementations of a certain service interface at once (such as obtaining the application startup callbacks of all modules and executing them in sequence), or can accurately obtain a specific implementation through filtering conditions. Compared with the traditional one-to-one service locator mode, this solution is more suitable for multi-module collaboration scenarios under modular architecture.
[0024] (3) This invention automatically scans and extracts service metadata through AST analysis and annotation processing. Developers can complete service registration by adding an annotation to the implementation class without manually writing any configuration files. The mapping table is generated and merged during compilation, avoiding the performance overhead of runtime scanning.
[0025] (4) This invention utilizes HarmonyOS native dynamic loading API to load modules and instantiate services on demand at runtime, supporting the addition or replacement of service implementations without modifying the caller's code; adding a new module only requires adding the @HMModularService annotation, the mapping table is automatically updated after compilation, and the new implementation is automatically discovered at runtime.
[0026] (5) The present invention flexibly controls the lifecycle of service instances through the singleton parameter of the annotation; stateless services use the singleton pattern to reduce memory overhead, and stateful services use the multi-instance pattern to ensure instance isolation; the instantiation strategy is determined when the service is declared, and the caller does not need to care.
[0027] (6) This invention manages the loading order and dependencies between modules through a global configuration file, ensuring the determinism of service loading and avoiding runtime exceptions caused by uncertain loading order. Attached Figure Description
[0028] Other features, objects, and advantages of the present invention will become more apparent from the following detailed description of non-limiting embodiments with reference to the accompanying drawings: Figure 1 This is a block diagram of the overall architecture of the system of the present invention; Figure 2 Flowchart for compile-time mapping table generation and merging; Figure 3 Flowchart for runtime service discovery and dynamic loading; Figure 4 This is a schematic diagram of the data structure of the global mapping table; Figure 5 A sequence diagram for a one-to-many service discovery scenario. Detailed Implementation
[0029] The present invention will now be described in detail with reference to specific embodiments. These embodiments will help those skilled in the art to further understand the present invention, but do not limit the invention in any way. It should be noted that those skilled in the art can make several changes and improvements without departing from the concept of the present invention. These all fall within the protection scope of the present invention.
[0030] Example 1 This invention proposes a one-to-many service discovery and dynamic loading system for HarmonyOS applications. This system runs in the HarmonyOS ArkTS runtime environment and includes five core modules: Module A: Service Annotation Module (HMModularDecorator), which provides compile-time service declaration decorators. Developers mark the ArkTS class as an implementation of a certain service interface by adding the @HMModularService decorator to the class declaration. The decorator parameters include the service path (used to identify the service interface), the singleton instantiation mode (marking whether it is a singleton), and the unique identifier id (used to distinguish a specific implementation among multiple implementations).
[0031] Module B: Compile-time mapping table generation module (HMModularPlugin). During the project compilation and build phase, it performs AST (Abstract Syntax Tree) analysis on the ArkTS / ETS source code using the TypeScript compiler API. It identifies all class declaration nodes marked with the @HMModularService decorator, extracts metadata such as service path, class name, module name, instantiation mode, and unique identifier, first generating a separate module-level mapping table file for each module, and then merging all module-level mapping tables into a single global mapping table file. Figure 4 The global mapping table is stored in key-value pairs, where the key is the service path and the value is an array of metadata of all implementations corresponding to the service path, thus establishing a one-to-many mapping relationship of "one service path corresponds to multiple implementations".
[0032] The specific rules for AST analysis are as follows: After creating an abstract syntax tree using the source file as the analysis unit, the class declaration nodes are recursively traversed, and only class declarations with the `@HMModularService` service declaration decorator are considered as candidate service implementation classes. For candidate classes, the object literal parameters in the decorator call expression are read, and fields such as `path`, `singleton`, and `id` are extracted. Simultaneously, the class name and source file path of the class declaration node are read. If the decorator parameter is a string, boolean value, or numeric literal, its value is directly recorded. If the decorator parameter references a constant or a static member of an object, the source file containing the reference is parsed according to the `import` declaration, and the corresponding constant value is further parsed in the referenced source file, thereby normalizing parameters such as the service path into serializable metadata. For complex relationships such as inheritance, interface implementation, and generics, the compile-time mapping table generation module determines registration based on whether the current class is marked by a service declaration decorator, without relying on runtime reflection information of parent classes, interfaces, or generic parameters. Inheritance relationships and generic constraints are verified by the ArkTS type system during the compilation phase, and the mapping table only stores the class name, path, and service declaration parameters necessary for dynamic loading. If the same class is decorated by multiple decorators, the analysis process checks the decorator name one by one, processing only the decorator whose name matches @HMModularService; other business decorators do not participate in service registration. If multiple @HMModularService decorators appear on the same class, conflict checking can be performed according to the pre-set rules during the build phase, and duplicate service declarations can be prompted to avoid the same implementation class being written to the mapping table repeatedly.
[0033] Module C: Mapping Table Loading and Parsing Module (HMMmapLoader), which is used to read the global mapping table file generated during the compilation phase during application startup and parse it into a mapping data structure in memory in the HarmonyOS ArkTS runtime environment; this module converts the JSON format mapping table into a Map data structure with service path as the key and implementation item array as the value for subsequent service queries.
[0034] Module D: Service Discovery and Filtering Module (HMMServiceProvider), which receives service query requests from callers, searches for all corresponding implementations in the memory-mapped data structure based on the service path in the request; when the caller provides filtering conditions, it filters the result set based on the metadata of the implementations (such as the id field), and returns a subset of implementations that meet the conditions, thereby realizing on-demand service acquisition under a one-to-many relationship.
[0035] In the current project implementation, the filtering mechanism doesn't directly index based on unique identifiers and achieve a single hit. Instead, it first finds a set of candidate implementations corresponding to the service path in the memory-mapped structure, and then executes the filtering conditions provided by the caller on each of these candidate implementations. In other words, the service path is used for the first layer of rapid location, while metadata such as unique identifiers is used for the second layer of conditional judgment. If the caller doesn't provide filtering conditions, all candidate implementations under that service path will be attempted to load sequentially. The current implementation doesn't build a separate auxiliary index for unique identifiers, nor does it perform additional index optimization for scenarios with hundreds or thousands of implementations under the same service path. Its performance guarantee mainly comes from first narrowing down the candidate range by service path, and then traversing and matching within the smaller candidate set. Furthermore, implementations typically originate from the granularity of business modules. The number of business modules within an application is inherently limited, and the common scenario of hundreds or thousands of business modules registering the same service path is unlikely. Therefore, the number of candidate implementations that need to be traversed under the same service path is controllable in actual business scenarios.
[0036] Module E: Dynamic Loading and Instantiation Module (HMMService), which receives the implementation items returned by the service discovery module. Based on the module path and class name recorded in the implementation item, it calls the native `napi_load_module_with_info` interface unique to the HarmonyOS ArkTS runtime to dynamically load the target HSP module. It obtains the constructor of the target class from the module's exported object and creates an instance object through the constructor. For implementation items marked as singleton mode, the instance is cached after the first creation, and subsequent requests directly return the cached instance. For non-singleton mode implementation items, a new instance is created for each request.
[0037] like Figure 1 The complete workflow for the above five modules to work together is as follows: Step 1 (Compilation-time - Service Declaration): Developers define service interfaces in each functional module and add the @HMModularService annotation to the implementation class. The path parameter of the annotation specifies the service path, which can be reused by multiple implementation classes in different modules. The `path` field in `@HMModularService` identifies the service interface path and is not directly equivalent to the module loading path required by `napi_load_module_with_info`. The compile-time mapping table generation module generates two different fields when scanning decorators: `servicePath` serves as the index key for service discovery, and `moduleLoadPath` serves as the path for dynamically loading the target module. `moduleLoadPath` is normalized from the source file path, the name of the module it belongs to, the module type, and the exported class name, thus avoiding the mixing of business service identifiers and physical module paths.
[0038] The path conversion rules include: First, identifying the module name and type of the source file based on its project directory. Module types include entry, feature, har, hsp, etc. Second, removing the src / main / ets prefix, file extension, and local project absolute path from the source code relative path, converting it to a module relative path recognizable by ArkTS runtime. Third, concatenating different loading prefixes based on the module type: for modules within the same HAP, using the entry module context and relative path; for HSP dynamic shared packages, using the HSP module name, package name, and module export path to form the loading description information. Finally, writing bundleName, entryModuleName, moduleName, moduleLoadPath, and className into the HMMItem. Dynamically loaded modules at runtime only use moduleLoadPath, bundleName, entryModuleName, and moduleName for location; servicePath is only used for service discovery.
[0039] Step 2 (Compilation-time – Metadata Scanning Based on ArkTS AST Analysis): The compilation-time mapping table generation module performs AST (Abstract Syntax Tree) syntax analysis on the ArkTS / ETS source code through the TypeScript compiler API, traversing the class declaration nodes (ClassDeclaration) in the AST, identifying all class declaration nodes decorated by the @HMModularService decorator, extracting parameters (path, singleton, id) and class name information from the decorator call expression, and determining the module name by combining the source file path; this step is executed in the compilation process of the HarmonyOS Ark compiler as a build plugin (HvigorPlugin), without the need for Java reflection mechanism; Step 3 (Compilation-time – Mapping Table Generation and Merging): For example... Figure 2 The compile-time mapping table generation module generates a module-level mapping table file HMMmap@{module}.json for each module. This file uses the service path as the key and the metadata array of the implementation items corresponding to the service path within the module as the value. Then, a merge task is executed to merge all module-level mapping tables into a global mapping table file HMMmap.json. During the merge, the implementation items of different modules under the same service path are gathered into the same array, forming the final "one-to-many" mapping relationship. Step 4 (Runtime – Mapping Table Loading): When the application starts, the mapping table loading and parsing module reads the global mapping table file and parses the JSON content into a data structure containing bundleName (application package name), entryModuleName (entry module name), and map (mapping dictionary), where map is a Map.<string, HMMItem[]> The type enables fast indexing from the service path to the array of implementation items; Step 5 (Runtime - Service Inquiry): For example Figure 3 The caller initiates a service query request through the service manager, specifying the target service path. The service discovery and filtering module searches the memory-mapped table based on the service path to obtain a list of all implementations corresponding to that service path. If the caller provides a filtering function, the filtering function is executed on each item in the list, filtering based on the implementation's ID or other metadata, and returning a subset of implementations that meet the conditions. If the caller does not provide a filtering function, the list of implementations is not filtered, and all implementations corresponding to that service path are returned directly, so that the caller can obtain all service implementations under the same service interface at once.
[0040] Step 6 (Runtime – Dynamic Loading and Instantiation): For each implementation item returned by the service query, the dynamic loading and instantiation module performs the following sub-steps: Step 6.1: Construct the complete loading path of the target module based on the module path (path field) and the module name (moduleName field) in the implementation item; Step 6.2: Call the native interface napi_load_module_with_info unique to the HarmonyOS ArkTS runtime, pass in the module path and application package information (bundleName), dynamically load the target HSP module, and obtain the module's exported object; this interface is different from Java's ClassLoader and Android's DexClassLoader, and is a module loading mechanism unique to the HarmonyOS ArkTS compiler runtime environment. The specific execution process includes: First, the dynamically loaded and instantiated module converts the relative module path in the implementation item into a module path recognizable by the ArkTS runtime, and combines information such as the application package name and entry module name into module context information; second, the module path and module context information are passed as string parameters to the NAPI method through the Native encapsulation layer, and parameter reading, encoding conversion, and validity verification are completed on the NAPI side; third, `napi_load_module_with_info` locates the target ArkTS module file within the current application package and its HAP / HSP modules based on the module path and module context information, and hands it over to the ArkTS runtime to perform module resolution, dependency resolution, and module initialization; then, the ArkTS runtime loads the compilation artifacts corresponding to the module, establishes the module execution context, and generates the exported object of the module; finally, the Native encapsulation layer returns the exported object to the ArkTS side, so that subsequent steps can obtain the target constructor from the exported object based on the class name. Through this process, the system can complete module location, loading, and export object acquisition according to the path recorded in the mapping table at the HarmonyOS ArkTS runtime without Java reflection or ClassLoader mechanism; The lifecycle of a module loaded by `napi_load_module_with_info` differs from that of the Java ClassLoader. This approach does not create independent class loader instances, nor does it rely on ClassLoader namespace isolation; the target module is resolved, initialized, and its execution context is maintained by the HarmonyOS runtime within the current application package and its HAP / HSP modules. To avoid duplicate loading, dynamic loading and instantiation of modules use a module export object cache, `ModuleExportCache`, which uses a combination of `bundleName`, `moduleName`, and `moduleLoadPath` as the cache key. If the same module path has already been successfully loaded, subsequent service instantiations directly reuse the cached export object, avoiding repeated calls to the underlying loading interface.
[0041] For the service object lifecycle, this solution uses a separate ServiceInstanceCache, which uses a combination of servicePath, id, moduleName, and className as the instance cache key. Instances are cached only for implementations where singleton=true; implementations where singleton=false are not cached, and a new instance is created for each query. This separates "module exported object reuse" from "service instance reuse," avoiding duplicate module loading or incorrect instance sharing due to different singleton strategies.
[0042] For unloading and resource release, this solution employs a secure logical unloading mechanism rather than forcibly unloading physical code. When a module is configured to be disabled or the application enters a phase requiring resource release, the system first stops service discovery for that module's implementation, then removes the corresponding service instance from the ServiceInstanceCache, and calls the implementation's optional dispose, onUnload, or equivalent release interfaces to unlisten, close connections, and release business resources; simultaneously, it clears the query result cache related to that module. Whether module exported objects in ModuleExportCache are released is managed uniformly by the Ark runtime and the application process lifecycle. The system forcibly unloads code modules when object references no longer exist, thus avoiding dangling references and runtime crashes.
[0043] In the current project implementation, exception handling is implemented at the outer layer of the dynamic loading and instantiation process. When a module fails to load, fails to obtain the target class's constructor entry point, throws an exception during construction, or encounters other loading exceptions, the system catches the exception within the scope of the current service implementation, generates a unified loading error message, and reports the failed implementation and the reason for the error to the outside world through the registered loading listeners. Failed implementations are not included in the final return result, nor are exceptions thrown to the caller, so the caller will not crash directly due to the loading failure of a single service implementation. If there are other candidate implementations under the same service path, the system will continue to process subsequent implementations; ultimately, the caller receives a set of successfully created service instances, or an empty result if no instances are available. Simultaneously, when the specified service path does not exist or no valid instance is obtained after filtering, the system triggers a path-level failure notification. The current source code does not have a complex transactional rollback mechanism; its failure handling can be summarized as: catching exceptions, reporting failures, skipping failed items, and preserving the normal loading results of other service implementations as much as possible.
[0044] Step 6.3: From the module-exported object, obtain the constructor reference of the target class by property access based on the class name (className field) of the implementation item; since ArkTS runtime does not support Java reflection mechanism, this step obtains the constructor through the property key-value mapping of the module-exported object instead of reflection call; Step 6.4: Create an instance of the target class using the constructor. The specific process includes: First, the dynamic loading and instantiation module verifies the validity of the constructor reference obtained in Step 6.3, confirming its existence and usability as the entry point for the ArkTS class construction. Second, the constructor reference is passed to a unified instantiation encapsulation method, which shields the ArkTS side from the differences in handling dynamic constructors directly. Third, the instantiation encapsulation method calls the target class constructor in a manner equivalent to `newConstructor()`, allocating an object instance, initializing member variables, and executing the construction logic for the target class during ArkTS runtime. Then, the generated object instance is returned to the dynamic loading and instantiation module as a service interface implementation object. Finally, the dynamic loading and instantiation module returns this instance to the caller according to the service interface type, enabling the caller to invoke the service capabilities provided by the target module through the interface methods. If the constructor reference is null, uncallable, or an exception occurs during construction, this step returns an instantiation failure result and triggers the subsequent loading failure handling process. Step 6.5: Determine the singleton flag (singleton field) of the implementation item. If it is true, the created instance will be cached, and subsequent requests for the implementation item will directly return the cached instance. If it is false, steps 6.1 to 6.4 will be executed to create a new instance each time.
[0045] This invention defines the following core data structures: Service Implementation Item (HMMItem):
[0046] Global Mapping Table (HMMmap):
[0047] Annotation parameter structure (HMModularServiceParam):
[0048] The detailed processing steps of the compile-time mapping table generation module are as follows: Step A1: Configure the scan path and determine the source code directory to be scanned (e.g., src / main / ets / services). Step A2: Traverse all .ets and .ts source files under the scan path, and use the TypeScript compiler API (ts.createSourceFile) to perform lexical and syntactic analysis on each file to construct an AST (Abstract Syntax Tree). This analysis is performed as a plugin in the HarmonyOS Hvigor build system, which is different from the APT (Annotation Processing Tool) annotation processor mechanism of the Java platform. Step A3: Recursively traverse the AST to find the class declaration node (ClassDeclaration), check whether the class is bound to a decorator, and whether the decorator's invocation expression name is HMModularService; decorators are a native syntax feature of the ArkTS / TypeScript language, which is different from Java's annotations; Step A4: For the matched class declaration node, extract the values of the path, singleton, and id fields from the decorator parameters, as well as the class name of the class declaration node; Step A5: Parse the module name to which the implementation class belongs based on the source file path; Step A6: Encapsulate the extracted metadata into an HMMItem object, and add the HMMItem to the array of the corresponding service path in the module-level mapping data, using the service path as the key; Step A7: Serialize the module-level mapping data into JSON format and write it to the module-level mapping table file HMMmap@{module}.json; Step A8: Scan all module-level mapping table files in the project and perform a merge operation: traverse each module-level mapping table, merge the mapping entries into the global mapping data, and append the implementation items with the same service path to the same array; Step A9: Serialize the merged global mapping data into JSON format and write it to the global mapping table file HMMmap.json. This file is packaged and distributed along with the application package.
[0049] The detailed steps for dynamically loading and instantiating modules at runtime are as follows: Step B1: Receive the HMMItem implementation item returned by the service discovery module; Step B2: Check the singleton flag of the implementation. If singleton is true and an instance of the implementation already exists in the cache, return the cached instance directly, and the process ends. Step B3: Based on the path and moduleName fields of the implementation item, construct the module loading path and convert the source code relative path into a path format that the module loader can recognize; Step B4: Call the loadModule interface provided by HarmonyOS runtime (based on napi_load_module_with_info), pass in the module path and application package information, load the target module, and obtain the module's exported object; Step B5: From the exported object of the module, use the className field of the implementation item as the key to obtain the constructor reference of the target class; Step B6: Call the newInstance method, passing in a constructor reference, to create an instance of the target class; Step B7: If the singleton flag of the implementation is true, store the created instance object in the cache with the unique identifier of the implementation as the key; Step B8: Return the instance object to the caller.
[0050] Supplementary Solution 1 – Module Dependency Management: The system further includes a module configuration management module, which manages the dependencies, loading priorities, and enabling status of each module through a global configuration file (hmm_global_config.json). The global configuration file defines each module's dependent module list (dependencies), loading priority value (higher values load first), and enabling flag (false skips loading the module's service). During the mapping table loading phase, the system sorts modules according to their priority values and loads the service implementations of each module sequentially from highest to lowest priority, checking whether its dependent modules have been loaded before loading a particular module.
[0051] Supplementary Solution 2 – Load Event Listening Mechanism: The system further provides a load event listening interface, allowing callers to register listeners to receive service loading success events (onLoaded) and service loading failure events (onModuleLoadFailed). A success callback is triggered when the service is successfully instantiated, passing implementation information; a failure callback is triggered when a module is not found, a class is not found, or an instantiation exception occurs during dynamic loading, passing implementation information and error details.
[0052] Supplementary Solution 3 – Dynamic Enabling and Disabling of Module Configuration: The system supports controlling whether the services of a specific module participate in discovery and loading through the `enable` field in the global configuration file. When `enable` is false, the compile-time mapping table generation module still scans the module and generates mapping data normally, but the runtime mapping table loading and parsing module skips all service implementation items of the module during the loading phase, thus achieving dynamic starting and stopping of the module without modifying the code.
[0053] Example 2: One-to-many service discovery for multi-module application startup callbacks Scenario description: The HarmonyOS application includes three business modules: home, quote, and trade. When the application starts, the initialization logic of each module needs to be executed in sequence.
[0054] like Figure 5 The specific implementation process is as follows: Step 1: Define the application startup callback service interface AppDelegate. This interface is used to define the initialization capabilities that each business module needs to execute during the application startup phase. The interface capability can be represented as: AppDelegate = {startup callback capabilities for initializing the application context}.
[0055] Step 2: Define the Home app Delegate class, which is the implementation class for the home page startup callback, in the home page module, and register it as an implementation of the App Delegate service through the service declaration parameters. The service path of this implementation is App Delegate, the instantiation mode is singleton, and the unique identifier is home. Its initialization responsibilities include loading home page cache data and registering home page components.
[0056] Step 3: Define the QuoteAppDelegate class, which implements the market data launch callback, in the market data module, and register it as an implementation of the AppDelegate service through the service declaration parameters. The service path of this implementation is AppDelegate, the instantiation mode is singleton, and the unique identifier is quote. Its initialization responsibilities include establishing a market data push connection and initializing the market data source.
[0057] Step 4: Define a transaction initiation callback implementation class TradeAppDelegate in the transaction module, and register it as an implementation of the AppDelegate service through the service declaration parameter; the service path of this implementation is AppDelegate, the instantiation mode is singleton, the unique identifier is trade, and its initialization responsibilities include loading transaction configuration and initializing transaction risk control.
[0058] Step 5: During compilation, the mapping table generation module scans the Home, Market Data, and Trading modules and merges them to generate a global mapping table. This mapping relationship can be represented as: M(AppDelegate) = [I_home, I_quote, I_trade], where I_home represents the HomeAppDelegate implementation of the Home module, I_quote represents the QuoteAppDelegate implementation of the Market Data module, and I_trade represents the TradeAppDelegate implementation of the Trading module; each implementation contains metadata such as module path, class name, singleton marker, unique identifier, and the name of the module to which it belongs.
[0059] Step 6: During runtime, the application entry module initiates a query using AppDelegate as the service path. Since no filtering conditions are provided, the service discovery module returns all implementation items in M (AppDelegate), and the dynamic loading and instantiation module creates three startup callback instances for homepage, market data, and trading respectively. Subsequently, the application entry module executes the initialization capabilities of each instance in sequence.
[0060] Step 7 (Filtered Call): If only the startup callback of the market data module needs to be loaded, the caller provides the filter condition F(item) when querying the AppDelegate service: item.id equals quote. The service discovery module applies the filter condition to each implementation item in M(AppDelegate), retaining only I_quote, and loads the startup callback instance corresponding to the market data module accordingly.
[0061] Example 3: Dynamic loading with module dependencies and priorities Scenario description: The trading module depends on data from the market data module, so it is necessary to ensure that the market data module is loaded before the trading module.
[0062] The specific implementation process is as follows: Step 1: Configure the global module configuration relationship. This configuration relationship can be represented as: C_module={home:(dependencies are empty, priority=1000, enable=true), quote: (dependencies are empty, priority=900, enable=true), trade: (dependencies=[quote], priority=800, enable=true)}. Here, dependencies represent the prerequisite dependencies that must be satisfied before the module is loaded, priority represents the loading priority when there are no dependency conflicts, and enable indicates whether the module participates in the loading process.
[0063] Step 2: During runtime, the system loads the service implementations of each module in descending order of priority. Before loading the trading module, it checks whether the market data module has been loaded; if not, it waits.
[0064] Step 3: When you need to temporarily disable the trading module, simply set the enable property of the trade module to false. No code modification or module removal is required. After recompiling, the service implementation of the trading module will not participate in discovery and loading.
[0065] Those skilled in the art will understand that, in addition to implementing the system, apparatus, and their modules provided by this invention in purely computer-readable program code, the same program can be implemented in the form of logic gates, switches, application-specific integrated circuits, programmable logic controllers, and embedded microcontrollers by logically programming the method steps. Therefore, the system, apparatus, and their modules provided by this invention can be considered a hardware component, and the modules included therein for implementing various programs can also be considered structures within the hardware component; alternatively, modules for implementing various functions can be considered both software programs implementing the method and structures within the hardware component.
[0066] Specific embodiments of the present invention have been described above. It should be understood that the present invention is not limited to the specific embodiments described above, and those skilled in the art can make various changes or modifications within the scope of the claims, which do not affect the essence of the present invention. Unless otherwise specified, the embodiments and features described in this application can be arbitrarily combined with each other.
Claims
1. A method for one-to-many service discovery and dynamic loading in HarmonyOS applications, characterized in that, include: Step 1: During the project compilation and build phase, the ArkTS / ETS source code is analyzed using the TypeScript compiler API. All class declaration nodes marked with the @HMModularService decorator are identified. The service path, class name, module name, instantiation mode, and unique identifier of the class declaration nodes are extracted as metadata. A module-level mapping table file is generated for each module. All module-level mapping table files are then merged into a global mapping table file. The global mapping table file is stored in key-value pairs, where the key is the service path and the value is an array of metadata of all implementations corresponding to that service path. Step 2: When the application starts, the global mapping table file is read and parsed into a mapping data structure in memory in the HarmonyOS ArkTS runtime environment; the mapping data structure is a Map data structure with service paths as keys and implementation item arrays as values; Step 3: Receive the service query request from the caller, and search for all corresponding implementation items in the memory-mapped data structure according to the service path in the request; when the caller provides filtering conditions, filter the result set according to the metadata of the implementation items, and return a subset of implementation items that meet the conditions; Step 4: Based on the module path and class name recorded in the returned implementation item, call the native interface napi_load_module_with_info unique to HarmonyOS ArkTS runtime to dynamically load the target HSP module, obtain the constructor of the target class from the module's exported object, and create an instance object through the constructor.
2. The method for one-to-many service discovery and dynamic loading in HarmonyOS applications according to claim 1, characterized in that, The specific rules for AST analysis are as follows: after creating an abstract syntax tree with the source file as the analysis unit, recursively traverse the class declaration nodes in it, and only class declarations with the @HMModularService service declaration decorator are selected as candidate service implementation classes; For candidate classes, read the object literal parameter in the decorator invocation expression, extract the path, singleton, and id fields, and read the class name and source file path in the class declaration node; if the decorator parameter is a string, boolean value, or numeric literal, record its value directly. If the decorator parameter references a constant or a static member of an object, the source file containing the reference is resolved according to the import declaration, and the corresponding constant value is further resolved in the referenced source file; if the same class is decorated by multiple decorators, only the decorator whose name matches @HMModularService is processed. If multiple @HMModularService decorators appear on the same class, conflict checks will be performed according to the default rules during the build process, and a message will be displayed indicating duplicate service declarations.
3. The method for one-to-many service discovery and dynamic loading in HarmonyOS applications according to claim 1, characterized in that, The path in @HMModularService is used to identify the service interface path. When scanning decorators, the compile-time mapping table generation module generates two different fields: servicePath is used as the index key for service discovery, and moduleLoadPath is used as the path for dynamically loading target modules. moduleLoadPath is obtained by standardizing the source file path, the name of the module, the module type, and the name of the exported class. The path conversion rules include: First, identifying the module name and module type of the source file based on the project directory where the source file is located. Module types include entry, feature, har, and hsp. Second, removing the src / main / ets prefix, file extension, and local project absolute path from the source code relative path and converting it into a module relative path that ArkTS can recognize at runtime. Next, different loading prefixes are concatenated according to the module type. For modules within the same HAP, the entry module context and relative path are used. For HSP dynamic shared packages, the HSP module name, package name and module export path are combined to form the loading description information. Finally, bundleName, entryModuleName, moduleName, moduleLoadPath and className are written together into HMMItem.
4. The method for one-to-many service discovery and dynamic loading in HarmonyOS applications according to claim 1, characterized in that, The process of calling the native interface `napi_load_module_with_info` includes: converting the module relative path in the implementation item into a module path recognizable by the ArkTS runtime, and combining the application package name and entry module name information into module context information; passing the module path and module context information as string parameters to the NAPI method through the Native wrapper layer; locating the target ArkTS module file within the current application package and its HAP / HSP modules based on the module path and module context information, and handing it over to the ArkTS runtime to perform module resolution, dependency resolution, and module initialization; loading the compilation artifacts corresponding to the module, establishing the module execution context, and generating the exported object of the module; and returning the exported object to the ArkTS side through the Native wrapper layer.
5. The method for one-to-many service discovery and dynamic loading in HarmonyOS applications according to claim 1, characterized in that, Set up a module export object cache ModuleExportCache, which uses a combination of bundleName, moduleName, and moduleLoadPath as the cache key; if the same module path has been successfully loaded, subsequent service instantiations will directly reuse the cached export object and will not repeatedly call the underlying loading interface. A separate ServiceInstanceCache is set up, which uses a combination of servicePath, id, moduleName, and className as the instance cache key. Instances are cached only for implementations with singleton=true; implementations with singleton=false are not cached, and a new instance is created for each query.
6. The method for one-to-many service discovery and dynamic loading in HarmonyOS applications according to claim 1, characterized in that, The dependencies, loading priorities, and enable status of each module are managed through the global configuration file hmm_global_config.json. The global configuration file defines the dependency list, loading priority value, and enable flag for each module. During the mapping table loading phase, the modules are sorted according to their priority values, and the service implementations of each module are loaded in descending order of priority. Before loading a module, it is checked whether its dependent modules have been loaded.
7. The method for one-to-many service discovery and dynamic loading in HarmonyOS applications according to claim 1, characterized in that, It provides a loading event listener interface, and the caller registers a listener to receive the service loading success event onLoaded and the service loading failure event onModuleLoadFailed. When the service is successfully instantiated, a success callback is triggered, which passes the implementation information. When a module is not found, a class is not found, or an instantiation exception occurs during dynamic loading, a failure callback is triggered, which passes the implementation information and error details.
8. A one-to-many service discovery and dynamic loading system in HarmonyOS applications, characterized in that, The method for one-to-many service discovery and dynamic loading in HarmonyOS applications according to any one of claims 1 to 7 includes: Service annotation module: Used to provide compile-time service declaration decorators. By adding the @HMModularService decorator to the ArkTS class declaration, the class is marked as an implementation of a certain service interface; the decorator parameters include service path, singleton instantiation mode, and unique identifier id; The compile-time mapping table generation module is used during the project compilation and build phase to perform AST syntax tree analysis on the ArkTS / ETS source code through the TypeScript compiler API, identify all class declaration nodes marked with the @HMModularService decorator, extract their metadata, first generate a module-level mapping table file for each module, and then merge all module-level mapping tables into a global mapping table file; the global mapping table file is stored in key-value pair format, where the key is the service path and the value is an array of metadata of all implementations corresponding to that service path; Mapping table loading and parsing module: used during application startup, to read the global mapping table file generated during compilation and parse it into a mapping data structure in memory in the HarmonyOS ArkTS runtime environment; the mapping table loading and parsing module converts the JSON format mapping table into a Map data structure with service path as key and implementation item array as value; Service discovery and filtering module: It is used to receive service query requests from callers, search for all corresponding implementation items in the memory-mapped data structure according to the service path in the request; when the caller provides filtering conditions, it filters the result set according to the metadata of the implementation items and returns a subset of implementation items that meet the conditions. Dynamic loading and instantiation module: This module receives the implementation items returned by the service discovery module. Based on the module path and class name recorded in the implementation item, it calls the native `napi_load_module_with_info` interface unique to the HarmonyOS ArkTS runtime to dynamically load the target HSP module. It obtains the constructor of the target class from the module's exported object and creates an instance object through the constructor. For implementation items marked as singletons, the instance is cached after the first creation, and subsequent requests directly return the cached instance. For non-singleton implementation items, a new instance is created for each request.
9. The one-to-many service discovery and dynamic loading system in HarmonyOS application according to claim 8, characterized in that, In the compiler mapping table generation module, the specific rules for AST analysis are as follows: after creating an abstract syntax tree with the source file as the analysis unit, the class declaration nodes are recursively traversed, and only the class declarations with the @HMModularService service declaration decorator are selected as candidate service implementation classes. For candidate classes, the object literal parameter in the decorator call expression is read, and the path, singleton, and id fields are extracted. At the same time, the class name and source file path of the class declaration node are read. For inheritance, interface implementation, and generic relationships, the compile-time mapping table generation module performs registration judgment based on whether the current class is marked by the service declaration decorator, without relying on runtime reflection information of parent class, interface, or generic parameters.
10. The one-to-many service discovery and dynamic loading system in HarmonyOS application according to claim 8, characterized in that, The dynamic loading and instantiation module also includes a logical unloading mechanism: when a module is configured to be disabled or the application enters a phase that requires resource release, the system first stops service discovery for the module's implementation, then removes the service instance corresponding to the module from the ServiceInstanceCache, and calls the optional dispose, onUnload, or equivalent release interface of the implementation; at the same time, it clears the query result cache related to the module.
Citation Information
Patent Citations
Hybrid architecture-based gap system adaptation method supporting dynamic expansion
CN121411953A
