1. 程式人生 > >使用JNI 呼叫第三方c++動態庫

使用JNI 呼叫第三方c++動態庫

昨天晚上到今天一直在折騰這個第三方庫檔案,唉,要哭了,一直就是各種問題。現在詳細說說怎麼做,踩過哪些坑。

現有個第三方的C++動態庫(libModel.so),這個libModel.so是要能被android呼叫的arm庫啊,需要在android中,使用java直接呼叫,那麼一般是兩種方式:

1:libModel.so 符合JNI規範,能夠直接在java層呼叫
2:libModel.so不符合規範,只是普通的c++動態庫,那麼只能是在JNI,寫個c/c++函式,呼叫這個libModel.so庫裡面的函式,然後重新編譯為libhello.so庫,android呼叫這個libModel.so;其實就像給libModel.so再封裝一層。

由於libModel.so不符合JNI規範,我只能採用第二種方式。
首先,建立一個HelloTest的android程式,然後在HelloTest中新建一個jni檔案,在jni檔案中仔建立一個prebuilt資料夾,裡面存放libmodel.so,並且新建一個Android.mk。
Android.mk的內容如下:

 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 LOCAL_MODULE := model
 LOCAL_SRC_FILES := libModel.so
 include $(PREBUILT_SHARED_LIBRARY
)

然後在jni裡面新建Android.mk

LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 LOCAL_MODULE := hello

 LOCAL_SRC_FILES := hello.cpp
 LOCAL_LDLIBS := -llog

 LOCAL_SHARED_LIBRARIES := model
 LOCAL_ALLOW_UNDEFINED_SYMBOLS := true //避免出現undefined的錯誤
 include $(BUILD_SHARED_LIBRARY)
include $(LOCAL_PATH)/prebuilt/Android.mk //包含prebuilt下的Android.mk

因為我jni裡面的具體函式使用到了,所以需要新增一個Application.mk,Application.mk內容如下:

APP_STL := stlport_static

在這裡也遇到了坑,一定要確保你是.cpp檔案,不然Application.mk沒有作用,依然會報:
error :can not find iostream的錯誤,因為這個stlport_static是供c++使用的。

在APK執行時,報瞭如下的錯誤,無法載入庫,那是因為apk沒有找到庫檔案:

01-02 03:50:05.655: E/AndroidRuntime(32314): Caused by: java.lang.UnsatisfiedLinkError: Cannot load library: reloc_library[1285]:  2824 cannot locate '_ZN13CVQASSESSMENTC1Ev'...

我之前嘗試使用如下方式進行打包,不建立prebuilt資料夾 ,直接在jni的Android.mk,加入libModel.so庫,:

 LOCAL_PATH := $(call my-dir)

 include $(CLEAR_VARS)
 LOCAL_MODULE := libModel
 LOCAL_SRC_FILES := libModel.so

 include $(PREBUILT_SHARED_LIBRARY)

 include $(CLEAR_VARS)
 LOCAL_MODULE  := modeljni 
 LOCAL_SRC_FILES := modeTest

 LOCAL_SHARED_LIBRARY  := libModel   

 include $(BUILD_SHARED_LIBRARY) 

以上面的方式打包,在apk執行的時候,就找不到庫,我也沒有想明白為什麼,還是我的打包有問題,用第一種方式執行成功

還有在java端呼叫庫的時候,兩個庫都要包含進去,且順序是先libModel.so,在呼叫新生成的。

static
    {
    System.loadLibrary("Model");
    System.loadLibrary("hello");
    }

其次就是c++庫需要傳遞結構體引數,我是這樣做的,在JAVA定義一個包含結構體所需引數的類,然後呢,java向jni中的C++ 傳遞 一個物件,c++解析java的物件,再重構第三方庫需要的結構體引數, java 與c 傳參 部落格寫的挺好的,具體的實現是:
首先在java申明native 方法:

public native int getInput(SideInfo sideInfo);

然後,使用javah 生成標頭檔案,將標頭檔案拷貝至jni中, jni中c++具體的實現如下:

//java 向c 傳遞物件
jint  Java_com_example_hellotest_MainActivity_getInput
(JNIEnv * env, jobject jthis, jobject sideInfo){
CVQASSESSMENT VqAssessUnit;
jmethodID methodId;
//獲得sideInfo物件的控制代碼
jclass cls_objClass=env->GetObjectClass(sideInfo);
//獲得sideInput物件中特定方法getI_Audio_sample_rate的id
methodId=env->GetMethodID(cls_objClass,"getI_Audio_sample_rate","()I");//第三個是方法的簽名,可以通過 javap -s XX.class的方式檢視方法的簽名。
//呼叫sideInfo物件的特定方法getI_Audio_sample_rate
jint  jrate=(jint)env->CallIntMethod(sideInfo,methodId,NULL); //獲取不同的值,有不同的CallXXMethod方法。
return jrate;
}

通過這樣的方法,就可以向c++ 傳遞java物件了。