DEV Community

Cover image for Load the native module in a static way
liu yang
liu yang

Posted on • Edited on

Load the native module in a static way

Loading Native Modules Statically

I. Overview

In modern application development, especially with the advent of ES6 and beyond, the ability to modularize code has become increasingly important. This modularization not only helps in maintaining clean and organized codebases but also enhances the reusability and scalability of the code. In ES6, the import syntax is used to load exported content from other files. This mechanism has been adapted in ArkTS to allow developers to conveniently import content exported from native modules (.so files).

Native modules are typically written in lower-level languages like C or C++ and compiled into shared libraries (.so files on Linux-based systems). These modules can provide high-performance functionalities that are not easily achievable with high-level languages like JavaScript or TypeScript. By enabling the import of native modules, ArkTS bridges the gap between high-level application logic and low-level performance-critical code.

II. Supported Import Methods

Direct Import

Direct import is the simplest and most straightforward method of importing content from native modules. This method involves exporting the content in the index.d.ts file of the native module and then directly importing it in your TypeScript file.

Named Import

// index.d.ts for libentry.so
export const add: (a: number, b: number) => number;

// test.ets
import { add } from 'libentry.so';
console.log(add(2, 3)); // Output: 5
Enter fullscreen mode Exit fullscreen mode

In this example, the add function is exported from the libentry.so native module. The index.d.ts file provides type definitions for the exported functions, allowing TypeScript to understand the function signatures and provide type checking.

Default Import

// index.d.ts for libentry.so
export const add: (a: number, b: number) => number;

// test.ets
import add from 'libentry.so';
console.log(add.add(2, 3)); // Output: 5
Enter fullscreen mode Exit fullscreen mode

Here, the add function is imported as the default export. Note that the actual function is accessed using add.add because the default export is an object containing the add function.

Namespace Import

// index.d.ts for libentry.so
export const add: (a: number, b: number) => number;

// test.ets
import * as add from 'libentry.so';
console.log(add.add(2, 3)); // Output: 5
Enter fullscreen mode Exit fullscreen mode

In this case, the entire module is imported as a namespace object. The add function is accessed using add.add.

Indirect Import

Indirect import involves exporting content from one file and then importing it from another file. This method can be useful for organizing code and creating intermediate layers of abstraction.

Export as Named Variables and Then Import

// test1.ets
import hilog from '@ohos.hilog';
export { hilog };

// test2.ets
import { hilog } from './test1';
hilog.info(0x000, 'testTag', '%{public}s', 'test');
Enter fullscreen mode Exit fullscreen mode

In this example, the hilog module is imported and re-exported in test1.ets. It is then imported in test2.ets and used to log information.

Export as Namespace and Then Import

// index.d.ts for libentry.so
export const add: (a: number, b: number) => number;

// test1.ets
export * from 'libentry.so';

// test2.ets
import { add } from './test1';
console.log(add(2, 3)); // Output: 5
Enter fullscreen mode Exit fullscreen mode

Here, all exports from libentry.so are re-exported in test1.ets and then imported in test2.ets.

Note: Using both named and namespace exports simultaneously for native modules is not supported. This can lead to conflicts and unexpected behavior.

Anti - example

// test1.ets
export * from 'libentry.so';

// test2.ets
import * as add from './test1';
// Unable to obtain the add object
Enter fullscreen mode Exit fullscreen mode

In this anti-example, attempting to import the entire namespace from test1.ets fails because the re-exported content from libentry.so is not correctly handled.

Dynamic Import

Dynamic imports allow modules to be loaded at runtime, providing more flexibility and enabling features like lazy loading. This method involves using the import() function, which returns a promise.

Direct Import

// index.d.ts for libentry.so
export const add: (a: number, b: number) => number;

// test.ets
import('libentry.so').then((ns: ESObject) => {
    console.log(ns.add(2, 3)); // Output: 5
});
Enter fullscreen mode Exit fullscreen mode

Here, the import() function is used to dynamically load the libentry.so module. The then method is used to handle the promise and access the add function.

Indirect Import

// test1.ets
import add from 'libentry.so';
export { add };

// test2.ets
import('./test1').then((ns: ESObject) => {
    console.log(ns.add(2, 3)); // Output: 5
});
Enter fullscreen mode Exit fullscreen mode

In this example, the add function is re-exported in test1.ets and then dynamically imported in test2.ets.

Note: Namespace exports are not supported when dynamically loading and exporting files. This can lead to issues where the imported namespace is not correctly populated.

Anti - example

// test1.ets
export * from 'libentry.so';

// test2.ets
import('./test1').then((ns: ESObject) => {
    // Unable to obtain the ns object
});
Enter fullscreen mode Exit fullscreen mode

In this anti-example, attempting to dynamically import the entire namespace from test1.ets fails because the re-exported content from libentry.so is not correctly handled.

III. Best Practices and Considerations

Type Definitions

Ensure that the index.d.ts file provides accurate type definitions for the exported functions. This helps TypeScript understand the function signatures and provides better type checking and autocompletion support.

Error Handling

When using dynamic imports, always include error handling to manage cases where the module fails to load. This can be done using the catch method on the promise.

import('libentry.so')
    .then((ns: ESObject) => {
        console.log(ns.add(2, 3)); // Output: 5
    })
    .catch((error) => {
        console.error('Failed to load module:', error);
    });
Enter fullscreen mode Exit fullscreen mode

Performance Considerations

Dynamic imports can improve performance by allowing lazy loading of modules. However, they can also introduce complexity and potential delays. Use them judiciously based on your application's requirements.

Testing

Thoroughly test your application to ensure that all imports and exports work as expected. Pay special attention to edge cases, such as importing from modules that may not be available at runtime.

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.