Symptom
Module A has a.so, and module B has b.so. The a.so file calls the functions of the b.so file, and the b.so file also calls the functions of the a.so file. The compilation fails no matter which .so file is compiled first.
Solution
Use dlopen and dlsym to decouple the compilation dependencies between the SO libraries and convert implicit dependencies to explicit dependencies. The sample code is as follows:
(Note that the function must be enclosed in extern "C" {}. Otherwise, the corresponding function cannot be identified, causing a compilation error.)
// a.cpp
extern "C" { // Be sure to enclose it with extern 'C' {}
#include "a.h"
#include <dlfcn.h>
#include "stdio.h"
typedef int (*FUNC_SUB)(int, int);
int add(int a, int b) { return a + b; }
int getb(char *path, int a, int b) { // Path:The sandbox path for passing So files from ArkTS side (note that the path should be passed from ArkTS side, otherwise it may not be found, and the specific code will be listed later)
void *handle = dlopen(path, RTLD_LAZY); // Open the dynamic link library with path as path
if (!handle) {
return 0;
}
FUNC_SUB sub_func = (FUNC_SUB)dlsym(handle, "sub"); // Get the function named sub
int res = sub_func(a, b); // caller function
dlclose(handle); // Close dynamic link library
return res;
}
} // a.h
extern "C" {
#ifndef DemoSO_a_H
#define DemoSO_a_H
int add(int a, int b);
int getb(char *path, int a, int b);
#endif // DemoSO_a_H
} // b.cpp
extern "C" { // Be sure to enclose it with extern 'C' {}
#include "b.h"
#include <dlfcn.h>
#include "stdio.h"
typedef int (*FUNC_ADD)(int, int);
int sub(int a, int b) { return a - b; }
int geta(char *path, int a, int b) { // Path: The sandbox path for passing So files from ArkTS side (note that the path should be passed from ArkTS side, otherwise it may not be found, and the specific code will be listed later)
void *handle = dlopen(path, RTLD_LAZY); // Open the dynamic link library with path as path
if (!handle) {
return 0;
}
FUNC_ADD add_func = (FUNC_ADD)dlsym(handle, "add"); // Get the function named sub
int res = add_func(a, b); // caller function
dlclose(handle); // Close dynamic link library
return res;
}
} // b.h
extern "C" {
#ifndef DemoSO_b_H
#define DemoSO_b_H
int sub(int a, int b);
int geta(char *path, int a, int b);
#endif // DemoSO_b_H
} # CMakeLists.txt cmake_minimum_required(VERSION 3.4.1) project(liba) # Compile library liba.so add_library(a SHARED a.cpp) target_link_libraries(a PUBLIC libace_napi.z.so libhilog_ndk.z.so) project(libb) # Compile library libb.so add_library(b SHARED b.cpp) target_link_libraries(b PUBLIC libace_napi.z.so libhilog_ndk.z.so)
The directory structure after the move is as follows:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
project(DemoSO)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
# Add libdemoso. so file
add_library(demoso SHARED hello.cpp)
# Add dependency libraries liba.so and libb.so. Please note to include the path, otherwise the corresponding SO library cannot be found
target_link_libraries(demoso PUBLIC libace_napi.z.so ${CMAKE_CURRENT_SOURCE_DIR}/../../../libs/${OHOS_ARCH}/liba.so ${CMAKE_CURRENT_SOURCE_DIR}/../../../libs/${OHOS_ARCH}/libb.so) // index.ets
import testNapi from 'libdemoso.so';
import { hilog } from '@kit.PerformanceAnalysisKit';
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
private path: string = '';
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
this.path = this.getUIContext().getHostContext()!.bundleCodeDir; // get path
hilog.info(0x0000, 'testTag', 'Test NAPI 5 + 3 = %{public}d', testNapi.add(5, 3, this.path + '/libs/arm64/liba.so')); // Call the native side function
hilog.info(0x0000, 'testTag', 'Test NAPI 5 - 3 = %{public}d', testNapi.sub(5, 3, this.path + '/libs/arm64/libb.so'));
})
}
.width('100%')
}
.height('100%')
}
} // index.d.ts export const add: (a: number, b: number, path: string) => number; export const sub: (a: number, b: number, path: string) => number;
// hello.cpp
#include "a.h"
#include "b.h"
#include "napi/native_api.h"
static napi_value Add(napi_env env, napi_callback_info info) {
size_t requireArgc = 3;
size_t argc = 3;
napi_value args[3] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
napi_valuetype valuetype0;
napi_typeof(env, args[0], &valuetype0);
napi_valuetype valuetype1;
napi_typeof(env, args[1], &valuetype1);
napi_valuetype valuetype2;
napi_typeof(env, args[2], &valuetype2);
int value0;
napi_get_value_int32(env, args[0], &value0);
int value1;
napi_get_value_int32(env, args[1], &value1);
char path[255];
size_t size = 255;
napi_get_value_string_utf8(env, args[2], path, 255, &size);
int res = geta(path, value0, value1); // Call the function and pass the sandbox path
napi_value sum;
napi_create_int32(env, res, &sum);
return sum;
}
static napi_value Sub(napi_env env, napi_callback_info info) {
size_t requireArgc = 3;
size_t argc = 3;
napi_value args[3] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
napi_valuetype valuetype0;
napi_typeof(env, args[0], &valuetype0);
napi_valuetype valuetype1;
napi_typeof(env, args[1], &valuetype1);
napi_valuetype valuetype2;
napi_typeof(env, args[2], &valuetype2);
int value0;
napi_get_value_int32(env, args[0], &value0);
int value1;
napi_get_value_int32(env, args[1], &value1);
char path[255];
size_t size = 255;
napi_get_value_string_utf8(env, args[2], path, 255, &size);
int res = getb(path, value0, value1); // Call the function and pass the sandbox path
napi_value sum;
napi_create_int32(env, res, &sum);
return sum;
}
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[] = {{"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr},
{"sub", nullptr, Sub, nullptr, nullptr, nullptr, napi_default, nullptr}};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "demoso",
.nm_priv = ((void *)0),
.reserved = {0},
};
extern "C" __attribute__((constructor)) void RegisterDemosoModule(void) { napi_module_register(&demoModule); }