文章目錄
- Android Jni(二)加載調用第三方 so 庫
- 前置知識
- CPU架構 ABI
- 基本步驟
- 1、將第三方 SO 庫文件放入項目中的正確位置:
- 2. 創建 JNI 接口
- 3. 實現 JNI 層代碼
- 4、配置 CMake
- 常見問題解決
- 1、UnsatisfiedLinkError:
- 2、函數找不到:
- 3、ABI 不匹配:
- 遇到的問題
- 1、include 找不到頭文件
- 2、Jni 不同庫的引入了重復 so,導致沖突
- 3、多個本地庫
- 4、return NewStringUTF(char*) 報格式錯誤
- 5、指針類型不一致報錯
- 6、Bitmap 轉 yuv 反色
- 7、接入的三方庫頭文件依賴了其他頭文件找不到
- 參考文章
Android Jni(二)加載調用第三方 so 庫
前置知識
CPU架構 ABI
接入第三方 so 庫時需要注意目標設備是否支持,不然會找不到 so 庫
abiFilters是用于指定在構建Android應用程序時應包含哪些CPU架構ABI(Application Binary Interface)的一種配置參數。它的常見取值包括"armeabi-v7a"、“arm64-v8a”、“x86”、"x86_64"等,具體取決于應用程序要支持的目標設備的CPU架構。在構建Gradle/Android項目時,可以通過在build.gradle配置文件中設置abiFilters來指定所需的CPU架構ABI。
基本步驟
1、將第三方 SO 庫文件放入項目中的正確位置:
app/src/main/jniLibs/armeabi-v7a/ // 32位 ARMlibthirdparty.soarm64-v8a/ // 64位 ARMlibthirdparty.sox86/ // x86libthirdparty.so
或者在 build.gradle 中指定庫的位置:
android {sourceSets {main {jniLibs.srcDirs = ['libs']}}
}
2. 創建 JNI 接口
public class NativeWrapper {static {// 先加載依賴庫(如果有)System.loadLibrary("dependency");// 然后加載目標庫System.loadLibrary("thirdparty");// 最后加載你自己的JNI庫System.loadLibrary("mylibrary");}// 聲明native方法public native int callThirdPartyFunction(int param);
}
3. 實現 JNI 層代碼
創建 jni/mylibrary.c 文件:
#include <jni.h>
#include <android/log.h>// 聲明第三方庫的函數
extern int third_party_function(int param);JNIEXPORT jint JNICALL
Java_com_example_NativeWrapper_callThirdPartyFunction(JNIEnv *env, jobject instance, jint param) {// 調用第三方庫函數int result = third_party_function(param);return (jint)result;
}
4、配置 CMake
add_library(thirdparty SHARED IMPORTED)
set_target_properties(thirdparty PROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libthirdparty.so)add_library(mylibrary SHAREDmylibrary.c)target_link_libraries(mylibrary thirdparty)
常見問題解決
1、UnsatisfiedLinkError:
檢查庫文件名是否正確(前綴 lib,后綴 .so)
檢查庫是否放在正確的 ABI 目錄下
檢查是否有依賴庫未加載
2、函數找不到:
使用 nm -D libthirdparty.so 檢查導出的函數名
可能需要 extern “C” 包裝
3、ABI 不匹配:
確保應用和所有庫使用相同的 ABI
64位設備可以運行32位庫,但反過來不行
遇到的問題
1、include 找不到頭文件
原因: include_directories 設置的路徑不對
解決方法:根據自己項目實際情況設置路徑
2、Jni 不同庫的引入了重復 so,導致沖突
確保項目中只包含一個libc++_shared.so版本。可以通過在項目的build.gradle文件中配置packagingOptions來選擇第一個找到的libc++_shared.so文件,使用pickFirst策略5。
3、多個本地庫
CMakeLists.txt 額外配置 target_link_libraries
target_link_libraries(local_lib1# List libraries link to the target libraryandroidlog)target_link_libraries(local_lib2# List libraries link to the target libraryandroidlog)
4、return NewStringUTF(char*) 報格式錯誤
對數組進行初始化賦值
char a[10] = {""};
5、指針類型不一致報錯
嚴格按照 api 文檔傳入指針
6、Bitmap 轉 yuv 反色
問題原因是如下代碼中,g b 數據通道,賦值反了,如何保存的 ARGB_8888 注釋中有寫。
#include <android/bitmap.h>
#include <jni.h>
#include <cstdint>
#include <cstring>// ARGB 轉 NV12 的函數
void ARGB_to_NV12(jint *argb, jbyte *nv12, jint width, jint height) {int frameSize = width * height;int yIndex = 0;int uvIndex = frameSize;for (int j = 0; j < height; ++j) {for (int i = 0; i < width; ++i) {int R = (argb[(j * width) + i] >> 16) & 0xFF;int G = (argb[(j * width) + i] >> 8) & 0xFF;int B = argb[(j * width) + i] & 0xFF;// 計算 YUV 分量int Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;int U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;int V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;// 存儲 Y 分量nv12[yIndex++] = static_cast<jbyte>((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));// 存儲 UV 分量(交錯存儲)if (j % 2 == 0 && i % 2 == 0) {nv12[uvIndex++] = static_cast<jbyte>((U < 0) ? 0 : ((U > 255) ? 255 : U));nv12[uvIndex++] = static_cast<jbyte>((V < 0) ? 0 : ((V > 255) ? 255 : V));}}}
}// JNI 函數
extern "C"
JNIEXPORT jbyteArray JNICALL
Java_com_example_YourClass_convertBitmapToNV12(JNIEnv *env, jobject thiz, jobject bitmap) {AndroidBitmapInfo info;void *pixels;jbyteArray nv12Array = nullptr;// 獲取 Bitmap 信息if (AndroidBitmap_getInfo(env, bitmap, &info) != ANDROID_BITMAP_RESULT_SUCCESS) {return nullptr;}// 檢查 Bitmap 格式是否為 RGBA_8888if (info.format != ANDROID_BITMAP_FORMAT_RGBA_8888) {return nullptr;}// 鎖定 Bitmap 像素數據if (AndroidBitmap_lockPixels(env, bitmap, &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) {return nullptr;}int width = info.width;int height = info.height;int frameSize = width * height;// 創建 NV12 數組(Y 分量占 width * height,UV 分量占 width * height / 2)nv12Array = env->NewByteArray(frameSize * 3 / 2);jbyte *nv12 = env->GetByteArrayElements(nv12Array, nullptr);// 將 ARGB 轉換為 NV12ARGB_to_NV12(static_cast<jint *>(pixels), nv12, width, height);// 釋放資源env->ReleaseByteArrayElements(nv12Array, nv12, 0);AndroidBitmap_unlockPixels(env, bitmap);return nv12Array;
}
7、接入的三方庫頭文件依賴了其他頭文件找不到
算法給的頭文件,可能會包含一些你不需要的代碼,之間刪除即可,不影響調用。
參考文章
Android jni引用第三方so動態庫和.a靜態庫并且調用?方法
Android JNI學習-調用第三方SO庫
Android 通過JNI調用三方so 高效教程
cmake使用詳細教程(日常使用這一篇就足夠了)