1. 程式人生 > 實用技巧 >IDEA+Clion JNI開發記錄

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, "", "()V");
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載入路徑