IDEA+Clion JNI開發記錄
最近由於專案需要,接觸瞭解了一部分jni相關開發,也踩了很多坑,包括結構體指標的使用以及c和java中傳遞,以及怎麼把java的實體類轉為c的結構體進而賦值返回。記錄一下方便後續查閱。
開發工具使用的idea和clion,idea主要用來編寫java相關程式碼並生成標頭檔案,clion則用來開發c相關程式碼以及生成.so庫檔案。
環境配置不做過多記錄,先從idea(java程式碼)部分開始。
1.新建專案
2.java程式碼
`public class JNIEntry {
static {
System.load("");
}
public native long init(String netModelPath, String transModelPath, String binModelPath, String norModelPath); public native Result recognize(long server, byte[] wav, int dataLength); public static void main(String[] args) { JNIEntry entry = new JNIEntry(); System.out.printf(""); }
}`
3.編譯java程式碼生成標頭檔案
配置一個idea中jni標頭檔案生成工具,然後右擊java類使用即可
4.clion中新建專案並使用cmake打包
我需要輸出動態庫,如下建立專案
專案目錄結構:
5.實現java中定義的兩個方法:
`JNIEXPORT jlong JNICALL Java_JNIEntry_init(
JNIEnv *env, jobject obc, jstring netPath, jstring transPath,
jstring binPath, jstring norPath) {
pvpr_instance_t bio = NULL; char* net_model = jstringToChar(env, netPath); char* trans_model = jstringToChar(env, transPath); char* bin_model = jstringToChar(env, binPath); char* nor_model = jstringToChar(env, norPath); //初始化服務例項 bio = create_pvpr_space(net_model,trans_model,bin_model, nor_model); if (NULL == bio) { fprintf(stderr, "create vpr space error!\n"); } else { fprintf(stderr, "create vpr space success!\n"); } pvpr_instance_t *pvpr = NULL; pvpr = &bio; **jlong* result = (jlong *) pvpr;** return *result;
}`
注:*這有一個坑卡了好久,服務控制代碼是以結構體指標表示,如何在java中拿到這個控制代碼並且反覆作為引數傳遞使用呢?
把struct *整個轉為jlong在c和java中互動
`JNIEXPORT jobject JNICALL Java_com_pachira_shared_identify_pvpr_core_PVPRIdentifierCore_recognize
(JNIEnv * env, jobject obj, jlong server, jbyteArray wav, jint length_wav) {
pvpr_instance_t bio;
bio = (pvpr_instance_t) server;
if (bio == NULL) {
fprintf(stderr,"invalid PachiraVpr server, please check\n");
return NULL;
}
char *data = JByteArrayToChars(env, wav);
pvpr_result_t result;
//辨認資料
result = pvpr_recognize_data(bio, data, (int)length_wav);
//資源釋放
if(data != NULL) {
free(data);
}
return convertJavaResult(env, result);
}`
注:這裡也有一個坑的點,怎麼把java定義的結果類作為c中的返回值返回
`//C result轉換為java結果類
jobject convertJavaResult(JNIEnv* env, pvpr_result_t cResult) {
jclass objectClass = env->FindClass(pvpr_result_class);
//構造結果類
jmethodID methodId =
env->GetMethodID(objectClass, "
jobject jResult = env->NewObject(objectClass, methodId);
//取出欄位
jfieldID uid = env->GetFieldID(objectClass, "uid", "Ljava/lang/String;");
jfieldID score = env->GetFieldID(objectClass, "score", "F");
jfieldID confidence = env->GetFieldID(objectClass, "confidence", "F");
//賦值
env->SetFloatField(jResult, score, cResult->score);
env->SetFloatField(jResult, confidence, cResult->confidence);
env->SetObjectField(jResult, uid, charArray2Jstring(env,cResult->uid));
return jResult;
}`
在c中構造結果的結構體,然後賦值給對應屬性最後返回
6.使用cmake編譯生成so庫
cMakeList檔案配置如下:
cmake_minimum_required(VERSION 3.15) //設定專案名稱 project(pvpr) //設定版本 set(CMAKE_CXX_STANDARD 14) //連結標頭檔案內容 include_directories(header) //這裡我引用了一個外部so庫,使用絕對路徑即可 link_libraries("xxxx/lib/vpr.so") //生成目標so庫 指定庫名,型別(動態庫還是靜態庫),實現cpp檔案,以及jni標頭檔案 add_library(jniTest SHARED JniEntry.cpp header/JniEntry.h)
直接在clion中run即可,編譯通過會生成so檔案
7.在java中使用即可
這裡我使用load載入可傳輸可配置的絕對路徑或者so檔案相對路徑。
loadLibray方法也可以,需要新增lib載入路徑