2015-08-24 81 views
2

我需要將C++文件集成到我的Android應用程序項目中。 我可以生成文件並生成.so文件。如何使用函數指針作爲Java的參數調用C++方法(Android)

這是具有函數process()的頭文件。 我需要從我的.java文件調用此方法。

​​

有兩個回調函數會給出結果和正在處理的數據的進度數據。 我無法爲它設計JNI方法。 順便說一句,我從.java文件運行簡單的C++程序。 但是這對我來說相當複雜。 請幫忙!!

進展 - 我創建了一個C++文件,並寫下了包裝

JNIEnv *global; 
jobject impl; 

struct DataProcessorStruct { 
jobject callback_ptr; 
DataProcessor *dataProcessor; 
}; 

void dataReadyCallback(std::vector<jint> processedSamples, jint heartRate){ 
jintArray arr = global->NewIntArray(processedSamples.size()); 
global->SetIntArrayRegion(arr, 0, processedSamples.size(), (jint *) &processedSamples[0]); 

jclass clazz = global->FindClass("com/app/AudioActivity"); 
jmethodID method = global->GetMethodID(clazz, "dataReady","[Ljava/util/List;I)V"); 
global->CallVoidMethod(impl,method,arr,heartRate); 
__android_log_print(ANDROID_LOG_INFO, "test", "C-dataReadyCallback"); 
} 

void progressCallback(jint progress){ 

jclass clazz = global->FindClass("com/app/AudioActivity"); 
jmethodID method = global->GetMethodID(clazz, "dataProcessProgress","(I)V"); 
global->CallVoidMethod(impl, method,progress); 
__android_log_print(ANDROID_LOG_INFO, "test", "C-progressCallback"); 
} 

JNIEXPORT jlong JNICALL Java_com_app_AudioActivity_createDataProcessorObject (JNIEnv * env, jobject obj){ 
global = env; 
impl = obj; 

DataProcessorStruct *cpp_obj = new DataProcessorStruct(); 

if (cpp_obj == NULL) { 
    return 0; 
} 

DataProcessor *csObj = new DataProcessor(dataReadyCallback,progressCallback); 
if (csObj == NULL) { 
    return 0; 
} 
cpp_obj->dataProcessor = csObj; 
cpp_obj->callback_ptr = env->NewGlobalRef(obj); 
return (jlong)cpp_obj; 
} 


JNIEXPORT void JNICALL Java_com_app_processData (JNIEnv * env, jobject obj,jlong objId,jint sample,jint dataLeft){ 
impl = obj; 
DataProcessorStruct *cpp_obj = (DataProcessorStruct *)objId; 
DataProcessor *dataProcessor=cpp_obj->dataProcessor; 
if (dataProcessor != NULL){ 
    dataProcessor->process(sample,dataLeft); 
} 
} 

本機方法是用Java -

public native long createDataProcessorObject(); 
public native void processData(long dataProcessor,int sample, int dataLeft); 

這是這樣做的正確方法? 是否有任何方法我不必直接從dataReadyCallback()和progressCallback()C++方法調用類Java方法,但以某種方式我可以調用Java中的接口方法以從這些C++方法調用,以便任何類監聽這些回調應該得到通知,而不是一個特定的類?

+0

您需要封送Java類型和C++之間的數據。查看[SWIG](http://www.swig.org/)。 –

+0

由於公司政策,我無法使用第三方軟件。 –

+0

如果你的公司禁止你使用工具來讓你的公司很有效率,那麼SWIG是一種產生代碼來編組語言之間的調用的工具。 –

回答

0

您可以輕鬆定義Java中的本地函數,這些函數在您的C++程序中是回調函數。通常你開始聲明類和一些功能在Java:

class MyJNative { 
    static { // static bloc, executed when class is loaded => load your cpp library 
     System.out.print ("Load library in "); 
     System.out.println(System.getProperty("java.library.path")); 
     System.loadLibrary("MyNativeCPP"); 
    } 

    private static int next_id=1; // every object gets a unique id 
    private int id;     // unique object id 

    public MyJNative() {   // constructor (sets unique id) 
     id = next_id++; 
     // ... 
    } 

    public native static void doSetup(); // initialisation for your factory function 
    public native void doSomething(); // example method 
    ... 
} 

然後你讓javah生成在C++本地funtions頭。你不應該自己手寫頭文件!這裏,對於上面的代碼步步tutorial,並且例如:

... 
#ifdef __cplusplus 
extern "C" { 
#endif 
/* 
* Class:  MyJNative 
* Method: doSetup 
* Signature:()V 
*/ 
JNIEXPORT void JNICALL Java_MyJNative_doSetup 
    (JNIEnv *, jclass); 

/* 
* Class:  MyJNative 
* Method: doSomething 
* Signature:()V 
*/ 
JNIEXPORT void JNICALL Java_MyJNative_doSomething 
    (JNIEnv *, jobject); 

...  
#ifdef __cplusplus 
} 
#endif 
#endif 

正如所看到的,JNI被設計爲調用C++/C的功能和C++類的不成員函數。所以你需要定義一些全局函數(或者你的類的靜態成員函數),它將得到一個可以識別C++對象的ID(第一個參數),然後將調用轉發給這個對象。

上面的例子被簡化了,但想法是,Java類保持唯一的ID。你可以想象,在創建時,在設置ID之後,它會調用C++中的工廠函數,它將保留map<jint, unique_ptr<MyCPPclass>> objects,以便可以使用ID作爲參數調用doSomething()之類的所有其他函數,然後調用objects[ID].get()->doSomething()

這種方法的問題是你的C++對象的生命:你管理它的破壞:在C++端創建的C++對象不會被垃圾回收,所以很難猜測它們何時不再需要。

這種方法的一個變體是主管你「託管它」在某個Java對象的字節數組中的某處。但在這種情況下,問題就出現了:相反,當不再需要Java對象時,C++方不會被告知對象被銷燬,因此析構函數將不會被調用。

正如您所看到的,JNI設計使管理雙方的對象變得相當困難。所以最安全的方法是僅在一側管理對象,另一側只公開使用/操作對象的函數。

+0

您能否爲上述問題提供一些代碼? –

+0

我知道這一點,但我的問題是不同的。我想調用的方法包含回調作爲參數,這就是我被阻止的地方。 –

+0

原理完全相同:回調指針是C++類數據。你需要一個本地包裝函數來查找/構造你的claa並調用你的成員函數指針。 – Christophe

相關問題