2017-09-03 83 views
0

我需要在Android項目中使用C++本機方法。 C++部分提供以下函數聲明:如何將ArrayList轉換爲指向JNI中指針的指針,以及如何將C++類轉換爲jobject?

void func(InputData **inputs, OutData *outputs); 

InputData和OutData是我自定義的C++類。該功能將從輸入讀取數據並將數據寫入輸出。我在Java中創建了兩個響應類:InputData和OutData。在Java中,我寫的函數聲明爲:

public static native OutData getOutData(ArrayList<InputData> inputDatas) 

在我的JNI代碼,它會被轉換爲:

JNIEXPORT jobject JNICALL JAVA_package_name_getOutData(JNIEnv *env, jclass clazz, jobject inputDatas) 

所以我的問題是,什麼是調用C正確的方法+功能?

我嘗試下面的代碼:

JNIEXPORT jobject JNICALL JAVA_package_name_getOutData(JNIEnv *env, jclass clazz, jobject inputDatas) { 
    InputData ** input = (InputData **)&inputDatas; 
    OutData *outData = (OutData *)malloc(neccessary_bytes); 
    func(input, outData); 
    return *outData; 
} 

我不知道它是否會因爲工作我現在不能運行項目。

EDIT:在C++ 類定義:

struct InputData { 
    uint64_t in_size; 
    uint8_t *data; 
} 

struct OutData { 
    uint64_t out_size; 
    uint8_t *data; 
} 

在Java:

public class InputData{ 
    public double in_size; 
    public byte[] data; 
} 

public class OutData { 
    public double out_size; 
    public byte[] data; 
} 
+1

只能通過強制轉換才能將C++類實例映射到Java類實例,反之亦然。請記住,每次你感受到投射的衝動時,這個投射通常是錯誤的工具,它告訴編譯器你知道你在做什麼。如果你不知道,那麼你只是誤導編譯器。我建議你從更簡單的事情開始。一個返回int的函數。當它工作時,試着在函數返回一個字符串,比如「Hello,殘忍的JNI世界!」。 –

+0

@ Cheersandhth.-Alf,所以我需要使用(* env) - > NewObject創建一個Java類實例,並將outData複製到它,對吧? – Tamarous

+0

就像那樣,是的。但不是直接嘗試,而是先做一些簡單的工作。總是一個好主意,以及不可思議的巨大流行的原因,否則這是無用的「你好,世界!」。 –

回答

0

鑑於(爪哇):

public class InputData { 
    public long in_size; 
    public byte[] data; 
} 

public class OutData { 
    public long out_size; 
    public byte[] data; 
} 

然後C++:

struct InputData 
{ 
    uint64_t in_size; 
    uint8_t *data; 
}; 

struct OutputData 
{ 
    uint64_t out_size; 
    uint8_t *data; 
}; 


OutputData* inputsToOutput(InputData** inputs, int numInputs) 
{ 
    //Do whatever here to convert them.. 
    return NULL; 
} 


//Convert a Java inputData to C++ inputData 
InputData* JavaInToCppIn(JNIEnv* env, jobject inputData) 
{ 
    jclass cls = env->GetObjectClass(inputData); 
    jlong in_size = env->GetLongField(inputData, env->GetFieldID(cls, "in_size", "J")); 
    jarray arr = static_cast<jarray>(env->GetObjectField(inputData, env->GetFieldID(cls, "data", "[B"))); 
    jsize arrSize = env->GetArrayLength(arr); 

    if (in_size != arrSize) 
    { 
     return NULL; 
    } 

    InputData* inData = new (std::nothrow) InputData(); 

    if (inData) 
    { 
     inData->in_size = in_size; 
     inData->data = new (std::nothrow) uint8_t[arrSize]; 

     if (inData->data) 
     { 
      void* ptr = env->GetPrimitiveArrayCritical(arr, NULL); 
      memcpy(inData->data, ptr, sizeof(uint8_t) * arrSize); 
      env->ReleasePrimitiveArrayCritical(arr, ptr, 0); 
      return inData; 
     } 

     delete inData; 
    } 
    return NULL; 
} 

//Convert a Java OutputData to C++ OutputData 
OutputData* JavaOutToCppOut(JNIEnv* env, jobject outputData) 
{ 
    jclass cls = env->GetObjectClass(outputData); 
    jlong out_size = env->GetLongField(outputData, env->GetFieldID(cls, "out_size", "J")); 
    jarray arr = static_cast<jarray>(env->GetObjectField(outputData, env->GetFieldID(cls, "data", "[B"))); 
    jsize arrSize = env->GetArrayLength(arr); 

    if (out_size != arrSize) 
    { 
     return NULL; 
    } 

    OutputData* outData = new (std::nothrow) OutputData(); 

    if (outData) 
    { 
     outData->out_size = out_size; 
     outData->data = new (std::nothrow) uint8_t[arrSize]; 

     if (outData->data) 
     { 
      void* ptr = env->GetPrimitiveArrayCritical(arr, NULL); 
      memcpy(outData->data, ptr, sizeof(uint8_t) * arrSize); 
      env->ReleasePrimitiveArrayCritical(arr, ptr, 0); 
      return outData; 
     } 

     delete outData; 
    } 
    return NULL; 
} 

//Convert a C++ InputData to Java InputData 
jobject CppInToJavaIn(JNIEnv* env, InputData* inData) 
{ 
    jclass cls = env->FindClass("Lpackage/Name/InputData;"); 
    jobject res = env->NewObject(cls, env->GetMethodID(cls, "<init>", "()V")); 
    env->SetLongField(res, env->GetFieldID(cls, "in_size", "J"), inData->in_size); 

    jbyte* ptr = reinterpret_cast<jbyte*>(inData->data); 
    jbyteArray arr = env->NewByteArray(inData->in_size); 
    env->SetByteArrayRegion(arr, 0, inData->in_size, ptr); 
    env->SetObjectField(res, env->GetFieldID(cls, "data", "[B"), arr); 

    return res; 
} 

//Convert a C++ OutputData to a Java OutputData 
jobject CppOutToJavaOut(JNIEnv* env, OutputData* outData) 
{ 
    jclass cls = env->FindClass("Lpackage/Name/OutputData;"); 
    jobject res = env->NewObject(cls, env->GetMethodID(cls, "<init>", "()V")); 
    env->SetLongField(res, env->GetFieldID(cls, "out_size", "J"), outData->out_size); 

    jbyte* ptr = reinterpret_cast<jbyte*>(outData->data); 
    jbyteArray arr = env->NewByteArray(outData->out_size); 
    env->SetByteArrayRegion(arr, 0, outData->out_size, ptr); 
    env->SetObjectField(res, env->GetFieldID(cls, "data", "[B"), arr); 

    return res; 
} 


//Get Inputs from an ArrayList, convert them to an OutputData. 
JNIEXPORT jobject JNICALL JAVA_package_name_getOutData(JNIEnv *env, jclass clazz, jobject inputDatas) 
{ 
    jclass arrayListClass = env->FindClass("Ljava/util/ArrayList;"); 
    int arrayListSize = env->CallIntMethod(inputDatas, env->GetMethodID(arrayListClass, "size", "()I")); 

    if (arrayListSize > 0) 
    { 
     //Array of C++ inputs from Java.. 
     InputData** inputs = new (std::nothrow) InputData*[arrayListSize]; 

     if (inputs) 
     { 
      for (int i = 0; i < arrayListSize; ++i) 
      { 
       jobject jinput = env->NewGlobalRef(env->CallObjectMethod(inputDatas, env->GetMethodID(arrayListClass, "get", "(I)Ljava/lang/Object;"), i)); 

       inputs[i] = JavaInToCppIn(env, jinput); 

       env->DeleteGlobalRef(jinput); 
      } 

      //Conver the C++ inputs to a C++ output, and then convert that C++ output into a Java OutputData. 
      OutputData* out = inputsToOutput(inputs, arrayListSize); 
      jobject output = CppOutToJavaOut(env, out); 


      //Clean-Up. 
      for (int i = 0; i < arrayListSize; ++i) 
      { 
       if (inputs[i]) 
       { 
        delete[] inputs[i]->data; 
       } 
       delete inputs[i]; 
      } 

      delete[] inputs; 
      delete out; 

      return output; 
     } 
    } 
    return NULL; 
} 

注:我用C++的newdelete,而不是mallocfree,但我一直是作爲nothrow這樣就可以‘檢查’如果分配成功,而不是它拋出異常。也請注意,我沒有使用std::unique_ptrstd::vector或任何構造函數/析構函數/ RAII的東西,因爲我不知道你對容器或任何東西的限制。

+0

非常感謝你,你的代碼是一種魔法!它運行得非常好,我甚至不需要修改一個角色。 – Tamarous

相關問題