#java #c #java-native-interface
Вопрос:
Я новичок в программировании JNI. Я создаю HashMap<String, ArrayList<Integer>>
файл на Java и передаю его в JNI, используя следующий блок кода:
#include <jni.h> // JNI header provided by JDK
#include <iostream> // C standard IO header
#include "HelloJNI.h" // Generated
#include <string>
#include <map>
#include <vector>
using namespace std;
// Implementation of the native method sayHello()
JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject thisObj, jstring nameObj, jobject hashMap) {
std::map<std::string, vector<int>> mapOut;
// Get the Map's entry Set.
jclass mapClass = env->FindClass("java/util/Map");
if (mapClass == NULL) {
return;
}
jmethodID entrySet = env->GetMethodID(mapClass, "entrySet", "()Ljava/util/Set;");
if (entrySet == NULL) {
return;
}
jobject set = env->CallObjectMethod(hashMap, entrySet);
if (set == NULL) {
return;
}
// Obtain an iterator over the Set
jclass setClass = env->FindClass("java/util/Set");
if (setClass == NULL) {
return;
}
jmethodID iterator =
env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;");
if (iterator == NULL) {
return;
}
jobject iter = env->CallObjectMethod(set, iterator);
if (iter == NULL) {
return;
}
// Get the Iterator method IDs
jclass iteratorClass = env->FindClass("java/util/Iterator");
if (iteratorClass == NULL) {
return;
}
jmethodID hasNext = env->GetMethodID(iteratorClass, "hasNext", "()Z");
if (hasNext == NULL) {
return;
}
jmethodID next =
env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");
if (next == NULL) {
return;
}
// Get the Entry class method IDs
jclass entryClass = env->FindClass("java/util/Map$Entry");
if (entryClass == NULL) {
return;
}
jmethodID getKey =
env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;");
if (getKey == NULL) {
return;
}
jmethodID getValue =
env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;");
if (getValue == NULL) {
return;
}
// Iterate over the entry Set
while (env->CallBooleanMethod(iter, hasNext)) {
jobject entry = env->CallObjectMethod(iter, next);
// retrieve key
jstring key = (jstring) env->CallObjectMethod(entry, getKey);
const char* keyStr = env->GetStringUTFChars(key, NULL);
if (!keyStr) { // Out of memory
return;
}
const char *keyobj = env->GetStringUTFChars(key, NULL);
// retrieve value
jobject jList = (jobject) env->CallObjectMethod(entry, getValue);
// retrieve the java.util.List interface class
jclass cList = env->FindClass("java/util/List");
// retrieve the size and the get method
jmethodID mSize = env->GetMethodID(cList, "size", "()I");
jmethodID mGet = env->GetMethodID(cList, "get", "(I)Ljava/lang/Object;");
if(mSize == NULL || mGet == NULL)
return;
// get the size of the list
jint size = env->CallIntMethod(jList, mSize);
// walk through and fill the vector
for(jint i=0;i<size;i ) {
jint intObj = (jint)env->CallIntMethod(jList, mGet, i);
mapOut[std::string(keyStr)].push_back(int(intObj));
//env->ReleaseStringUTFChars(intObj);
}
env->DeleteLocalRef(entry);
env->ReleaseStringUTFChars(key, keyStr);
env->DeleteLocalRef(key);
}
for(map<string, vector<int>>::iterator ii=mapOut.begin(); ii!=mapOut.end(); ii){
cout << (*ii).first << ": ";
vector <int> inVect = (*ii).second;
for (unsigned j=0; j<inVect.size(); j ){
cout << inVect[j] << " ";
}
cout << endl;
}
const char *name = env->GetStringUTFChars(nameObj, NULL);
cout << "Hello " << name << " from C !" << endl;
return;
}
Вывод кода выглядит следующим образом:
Canada: 2311168 2311176 2311184
England: 2311272 2311280 2311288 2311296
Pakistan: 2311224 2311232
Hello John from C !
[{Canada=[22, 66, 99], Pakistan=[34, 6], England=[4, 20, 100, 55]}]
Хэш-карта в java успешно печатается. Однако я не могу получить целочисленные значения массива в JNI.
Комментарии:
1. Вы не сказали нам, что в вашем
ArrayList
содержимом. В любом случае, в вашем звонкеGetMethodID
вам указали , чтоget
вернут anObject
, поэтому вам следует использоватьCallObjectMethod
, а неCallIntMethod
. И, конечно, вы не можете преобразовать JavaObject
в Cint
.2. Не связано: вы можете сэкономить немного времени на вводе, используя
GetObjectClass
полученные объекты вместо вызоваFindClass
.
Ответ №1:
Предполагая, что у вас есть ArrayList<Integer>
значение в каждой хэш-карте, вам нужно вызвать .intValue()
метод для каждого целочисленного объекта:
Таким образом:
jclass integerClass = env->FindClass("java/lang/Integer");
jmethodID mIntValue = env->GetMethodID(integerClass, "intValue", "()I");
// ...
for(jint i=0;i<size;i ) {
jobject integer = env->CallObjectMethod(jList, mGet, i);
int intObj = env->CallIntMethod(integer, mIntValue);
mapOut[std::string(keyStr)].push_back(intObj);
env->DeleteLocalRef(integer);
}
Ответ №2:
Все это (и многое другое) может быть сделано автоматически с помощью языкового моста Scapix:
C
class test : public scapix::bridge::object<test>
{
public:
static void func(std::map<std::string, std::vector<int>> map);
};
Java (сгенерированный Scapix)
public class Test extends com.scapix.Bridge
{
public native static void func(java.util.TreeMap<java.lang.String, int[]> map);
}
Теперь вы можете вызвать эту сгенерированную функцию Java, и все параметры и возвращаемые значения будут автоматически преобразованы.
Отказ от ответственности: Я являюсь автором языкового моста Scapix