Java (JNI) → func.c (JNI 入口) → func1.c (C 邏輯層) → func2.so/dll (動態庫)
`func1.c` 如何調用 `func2.so` (Linux) / `func2.dll` (Windows) 中的函數?
// func1.c
#include
#include "func2.h" // 聲明頭文件
// 函數指針類型定義
typedef int (*Func2_Add_t)(int, int);
typedef void (*Func2_Init_t)(void);
static HINSTANCE hFunc2Dll = NULL;
static Func2_Add_t pFunc2_Add = NULL;
static Func2_Init_t pFunc2_Init = NULL;
BOOL LoadFunc2Library() {
if (hFunc2Dll) return TRUE; // 已加載
// 1. 加載 DLL (路徑: 同目錄、系統目錄、PATH 環境變量)
hFunc2Dll = LoadLibrary("func2.dll"); // 或完整路徑
if (!hFunc2Dll) {
DWORD err = GetLastError();
// 記錄錯誤
return FALSE;
}
// 2. 獲取函數地址
pFunc2_Add = (Func2_Add_t)GetProcAddress(hFunc2Dll, "Func2_Add");
pFunc2_Init = (Func2_Init_t)GetProcAddress(hFunc2Dll, "Func2_Init");
if (!pFunc2_Add || !pFunc2_Init) {
FreeLibrary(hFunc2Dll);
hFunc2Dll = NULL;
return FALSE;
}
// 3. 初始化 (可選)
pFunc2_Init();
return TRUE;
}
void UnloadFunc2Library() {
if (hFunc2Dll) {
FreeLibrary(hFunc2Dll);
hFunc2Dll = NULL;
pFunc2_Add = pFunc2_Init = NULL;
}
}
// 使用示例
int CallFunc2Add(int a, int b) {
if (!LoadFunc2Library()) return -1;
return pFunc2_Add(a, b);
}
// func1.c
#include
#include "func2.h"
typedef int (*Func2_Add_t)(int, int);
typedef void (*Func2_Init_t)(void);
static void* hFunc2So = NULL;
static Func2_Add_t pFunc2_Add = NULL;
static Func2_Init_t pFunc2_Init = NULL;
int LoadFunc2Library() {
if (hFunc2So) return 1;
// 1. 加載 SO (RTLD_LAZY/RTLD_NOW)
// 路徑: ./func2.so、/system/lib64/、LD_LIBRARY_PATH、rpath
hFunc2So = dlopen("func2.so", RTLD_NOW);
if (!hFunc2So) {
const char* err = dlerror();
// 記錄錯誤
return 0;
}
// 2. 獲取函數指針 (dlsym 返回 void*)
pFunc2_Add = (Func2_Add_t)dlsym(hFunc2So, "Func2_Add");
pFunc2_Init = (Func2_Init_t)dlsym(hFunc2So, "Func2_Init");
const char* symErr = dlerror();
if (symErr || !pFunc2_Add || !pFunc2_Init) {
dlclose(hFunc2So);
hFunc2So = NULL;
return 0;
}
pFunc2_Init();
return 1;
}
void UnloadFunc2Library() {
if (hFunc2So) {
dlclose(hFunc2So);
hFunc2So = NULL;
pFunc2_Add = pFunc2_Init = NULL;
}
}
| 問題 | 解決 |
|---|---|
| 庫文件查找路徑 | Windows: 應用目錄→系統目錄→PATH。Linux: `LD_LIBRARY_PATH`、rpath (`-Wl,-rpath,$ORIGIN`)、`/system/lib64` (Android 系統庫)。建議將 `func2.so` 放在 `jniLibs/ |
| C++ 名稱修飾 | 若 `func2.cpp` 定義函數,**必須用 `extern "C"`** 防止名稱修飾,否則 `GetProcAddress`/`dlsym` 找不到符號。 `extern "C" int Func2_Add(int a, int b) { ... }` |
| 線程安全 | 加載/卸載函數需加鎖 (互斥量/原子標誌),防止并發重複加載/卸載。 |
| JNI 線程附著 | 若 `func2` 回調 Java,需 `AttachCurrentThread` 獲取 `JNIEnv*`。 |
| 錯誤處理 | 記錄 `GetLastError()` / `dlerror()` 具體信息,便於診斷「找不到庫」「符號缺失」「依賴缺失」。 |