隨著 Android Studio 逐漸棄用 `ndk-build` 轉向 CMake,JNI 開發流程發生重大變化。本文基於最新版 AS (Gradle 4.0+、CMake 3.10+),詳細演示從聲明 `native` 方法到生成 `.so` 庫的標準化流程,並針對常見坑點給出解決方案。
Java/Kotlin 類聲明 native 方法
↓
Build → Make Project 生成 .class
↓
javah / javac -h 生成 C/C++ 頭文件 (.h)
↓
編寫 .c/.cpp 實現對應函數
↓
CMakeLists.txt 配置編譯腳本 (模塊名、源文件、鏈接庫)
↓
Gradle 同步 → 自動調用 CMake → 編譯生成 libxxx.so
↓
Java/Kotlin 中 System.loadLibrary("xxx") 載入調用
// JniUtil.java
package com.example.jnidemo;
public class JniUtil {
static {
System.loadLibrary("native-lib"); // 對應 CMake 模塊名
}
public native String getStringFromNative();
}
最佳實踐:不要在 Activity 中聲明 native 方法。獨立類便於單元測試、避免生命周期耦合、生成頭文件時包名更穩定。
AS 終端執行:
# 進入模塊目錄
cd app
# 生成頭文件至 src/main/cpp (或自定義目錄)
javac -h src/main/cpp src/main/java/com/example/jnidemo/JniUtil.java
產出:`com_example_jnidemo_JniUtil.h`,包含函數簽名:
`JNIEXPORT jstring JNICALL Java_com_example_jnidemo_JniUtil_getStringFromNative(JNIEnv *, jobject);`
// native-lib.cpp
#include
#include "com_example_jnidemo_JniUtil.h"
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_jnidemo_JniUtil_getStringFromNative(JNIEnv *env, jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
cmake_minimum_required(VERSION 3.4.1)
# 定義模塊名,對應 System.loadLibrary("native-lib")
add_library(native-lib SHARED native-lib.cpp)
# 查找 Android 日志庫
find_library(log-lib log)
# 鏈接
target_link_libraries(native-lib ${log-lib})
android {
...
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}
}
// 指定 NDK 版本 (可選,建議鎖定)
ndkVersion "21.4.7075529"
}
| 問題 | 原因 | 解決 |
|---|---|---|
| UnsatisfiedLinkError | 庫名不匹配 / ABI 不支援 | 1. `loadLibrary` 名 = `add_library` 名 2. `defaultConfig.ndk.abiFilters` 指定 arm64-v8a/armeabi-v7a |
| 找不到頭文件 | 包名變更 / 生成路徑錯誤 | 重新執行 `javac -h`,或在 CMake 中 `include_directories(${CMAKE_SOURCE_DIR}/../java)` |
| CMake 編譯失敗 | NDK 版本不兼容 / CMake 版本過低 | Gradle 中鎖定 `ndkVersion`、`cmake.version` |
| 符號重複定義 | 多模塊編譯同名 .c 文件 | 模塊名唯一、源文件路徑隔離 |