I am having problems working with JNI and have been stuck on this issue for quite some time. I have posted about this before, but never received an answer and have done lots of research between now and then.
My JNI signature:
JNIEXPORT void JNICALL Java_MyApplet_invokeManager(JNIEnv *jniEnvPtr, jobject javaObj, jbyteArray encodedData)
Some of my code:
boolean isCopy;
jbyte* bytes = jniEnvPtr->GetByteArrayElements(encodedData, &isCopy);
jniEnvPtr->ReleaseByteArrayElements(encodedData, bytes, JNI_ABORT);
myManager->ShowQueue(encodedData);
The error message:
error C2664: 'MyModule::JniToManaged::ShowFormQueue' : cannot convert parameter 2 from 'jbyte *' to 'cli::array<Type,dimension> ^'
I have found no way to convert this to the C# byte[] that I need to pass. I have heard about casting the jbyte* object but can't figure out how to get it into the correct format.
I figured out how to convert a jbytearray to cli::array. Here is the code:
jbytearray jArray; //my array
jint len = jniEnvPtr->GetArrayLength(jArray); //get length
boolean isCopy;
jbyte* b = jniEnvPtr->GetByteArrayElements(jArray, &isCopy); //get pointer
array<byte, 1> ^myArray = gcnew array<byte, 1>(len); //create the cli::array
//loop through jbytearray and copy elements into our cli::array
for(int i = 0; i < len; i++)
{
myArray[i] = b[i];
}
jniEnvPtr->ReleaseByteArrayElements(jArray, b, JNI_ABORT); // release
I also had to convert a jstring to System::String^...
jboolean blnIsCopy;
jstring jstrOutput;
//jstring to char*
const char* strA = (jniEnvPtr)->GetStringUTFChars(theJString, &blnIsCopy);
//char* to std::string
std::string standardStr(strA);
//std:string to System::String^
System::String^ str2 = gcnew System::String(standardStr.c_str())
MessageBox(NULL, standardStr.c_str(), "Report Name!", MB_OK);
jniEnvPtr->ReleaseStringUTFChars(theJString, strA);
If there are no answers, it means (a) nobody knows, (b) nobody cares (Posting .net stuff under C++ is a sure fire way to get there. Posting an error message that should be easy to fix is another.), or (c) it might be a case of "What the heck is OP trying to do?! This makes no sense whatsoever. Nope, no head nor tail. Moving on..."
Related
I am attempting to use the android NDK.
Is there a way to return an array (in my case an int[]) created in JNI to Java? If so, please provide a quick example of the JNI function that would do this.
-Thanks
If you've examined the documentation and still have questions that should be part of your initial question. In this case, the JNI function in the example creates a number of arrays. The outer array is comprised of an 'Object' array creating with the JNI function NewObjectArray(). From the perspective of JNI, that's all a two dimensional array is, an object array containing a number of other inner arrays.
The following for loop creates the inner arrays which are of type int[] using the JNI function NewIntArray(). If you just wanted to return a single dimensional array of ints, then the NewIntArray() function is what you'd use to create the return value. If you wanted to create a single dimensional array of Strings then you'd use the NewObjectArray() function but with a different parameter for the class.
Since you want to return an int array, then your code is going to look something like this:
JNIEXPORT jintArray JNICALL Java_ArrayTest_initIntArray(JNIEnv *env, jclass cls, int size)
{
jintArray result;
result = (*env)->NewIntArray(env, size);
if (result == NULL) {
return NULL; /* out of memory error thrown */
}
int i;
// fill a temp structure to use to populate the java int array
jint fill[size];
for (i = 0; i < size; i++) {
fill[i] = 0; // put whatever logic you want to populate the values here.
}
// move from the temp structure to the java structure
(*env)->SetIntArrayRegion(env, result, 0, size, fill);
return result;
}
if someone would like to know how to return String[] array:
java code
private native String[] data();
native export
JNIEXPORT jobjectArray JNICALL Java_example_data() (JNIEnv *, jobject);
native code
JNIEXPORT jobjectArray JNICALL
Java_example_data
(JNIEnv *env, jobject jobj){
jobjectArray ret;
int i;
char *message[5]= {"first",
"second",
"third",
"fourth",
"fifth"};
ret= (jobjectArray)env->NewObjectArray(5,
env->FindClass("java/lang/String"),
env->NewStringUTF(""));
for(i=0;i<5;i++) {
env->SetObjectArrayElement(
ret,i,env->NewStringUTF(message[i]));
}
return(ret);
}
from link:
http://www.coderanch.com/t/326467/java/java/Returning-String-array-program-Java
Based on the asked question, this is already explained in the first answer that how can we pass int[] via jobjectArray. But Here is an example how we can return a jobjectArray which contains lists of data. This can be helpful for situations for example: when someone needs to return data in 2D format to draw some line with x and y points. The below example shows how a jobjectArray can return data in the form of following format:
Java input to the JNI:
Array[Arraylist of x float points][Arraylist of y float points]
JNI output to java:
jobjectArray[Arraylist of x float points] [Arraylist of y float points]
extern "C" JNIEXPORT jobjectArray JNICALL
_MainActivity_callOpenCVFn(
JNIEnv *env, jobject /* this */,
jobjectArray list) {
//Finding arrayList class and float class(2 lists , one x and another is y)
static jclass arrayListCls = static_cast<jclass>(env->NewGlobalRef(env->FindClass("java/util/ArrayList")));
jclass floatCls = env->FindClass("java/lang/Float");
//env initialization of list object and float
static jmethodID listConstructor = env->GetMethodID(arrayListCls, "<init>", "(I)V");
jmethodID alGetId = env->GetMethodID(arrayListCls, "get", "(I)Ljava/lang/Object;");
jmethodID alSizeId = env->GetMethodID(arrayListCls, "size", "()I");
static jmethodID addElementToList = env->GetMethodID(arrayListCls, "add", "(Ljava/lang/Object;)Z");
jmethodID floatConstructor = env->GetMethodID( floatCls, "<init>", "(F)V");
jmethodID floatId = env->GetMethodID(floatCls,"floatValue", "()F");
//null check(if null then return)
if (arrayListCls == nullptr || floatCls == nullptr) {
return 0;
}
// Get the value of each Float list object in the array
jsize length = env->GetArrayLength(list);
//If empty
if (length < 1) {
env->DeleteLocalRef(arrayListCls);
env->DeleteLocalRef(floatCls);
return 0;
}
// Creating an output jObjectArray
jobjectArray outJNIArray = env->NewObjectArray(length, arrayListCls, 0);
//taking list of X and Y points object at the time of return
jobject xPoint,yPoint,xReturnObject,yReturnObject;
//getting the xList,yList object from the array
jobject xObjFloatList = env->GetObjectArrayElement(list, 0);
jobject yObjFloatList = env->GetObjectArrayElement(list, 1);
// number of elements present in the array object
int xPointCounts = static_cast<int>(env->CallIntMethod(xObjFloatList, alSizeId));
static jfloat xReturn, yReturn;
jobject xReturnArrayList = env->NewObject(arrayListCls,listConstructor,0);
jobject yReturnArrayList = env->NewObject(arrayListCls,listConstructor,0);
for (int j = 0; j < xPointCounts; j++) {
//Getting the x points from the x object list in the array
xPoint = env->CallObjectMethod(xObjFloatList, alGetId, j);
//Getting the y points from the y object list in the array
yPoint = env->CallObjectMethod(yObjFloatList, alGetId, j);
//Returning jobjectArray(Here I am returning the same x and points I am receiving from java side, just to show how to make the returning `jobjectArray`)
//float x and y values
xReturn =static_cast<jfloat >(env->CallFloatMethod(xPoint, floatId,j));
yReturn =static_cast<jfloat >(env->CallFloatMethod(yPoint, floatId,j));
xReturnObject = env->NewObject(floatCls,floatConstructor,xReturn);
yReturnObject = env->NewObject(floatCls,floatConstructor,yReturn);
env->CallBooleanMethod(xReturnArrayList,addElementToList,xReturnObject);
env->CallBooleanMethod(yReturnArrayList,addElementToList,yReturnObject);
env->SetObjectArrayElement(outJNIArray,0,xReturnArrayList);
env->SetObjectArrayElement(outJNIArray,1,yReturnArrayList);
__android_log_print(ANDROID_LOG_ERROR, "List of X and Y are saved in the array","%d", 3);
}
return outJNIArray;
Simple solution is that write the array data in a file from C,and then access the file from Java
I'm trying to integrate a 3rd party C library into my project, I've never done this before and experiencing a problem with something that should be so simple, passing a string value to the C function.
Below is my code and the line that is failing generates the following error:
'Implicit conversion of an Objective-C pointer to 'const char *' is disallowed with ARC'
My Code:
NSString *myMapCode = #"GBR H4J.XLL";
double lat = 0.0;
double lng = 0.0;
returnValue = mc2coord(&lat, &lng, "GBR H4J.XLL", 0); // This works perfectly
returnValue = mc2coord(&lat, &lng, myMapCode, 0); // This is my Problem code
Thanks for your help.
Here you pass an NSString, not a C string:
returnValue = mc2coord(&lat, &lng, myMapCode, 0);
If that function expects a C string, you can either change
NSString *myMapCode = #"GBR H4J.XLL";
to
const char *myMapCode = "GBR H4J.XLL";
or convert the NSString to a C string:
NSString *myMapCode = #"GBR H4J.XLL";
returnValue = mc2coord(&lat, &lng, [myMapCode UTF8String], 0);
The mc2coordfunction does not take an NSString object, but rather a C string, or const char *. Above, don't save myMapCode as an NSString but rather the C string and you will be fine.
I have a Java class that returns a unicode string... Java has the correct version of the string but when it comes through a JNI wrapper in the form of a jstring it must be converted over to a C++ or C++/CLI string. Here is some test code I have which actually works on most languages except for the asian char sets. Chinese Simplified & Japanese characters are garbled and I can't figure out why. Here is the code snippet, I don't see anything wrong with either methods of conversion (the if statement checks os as I have two VMs with diff OS's and runs the appropriate conversion method).
String^ JStringToCliString(const jstring string){
String^ converted = gcnew String("");
JNIEnv* envLoc = GetJniEnvHandle();
std::wstring value;
jboolean isCopy;
if(string){
try{
jsize len = env->GetStringLength(string);
if(Environment::OSVersion->Version->Major >= 6) // 6 is post XP/2003
{
TraceLog::Log("Using GetStringChars() for string conversion");
const jchar* raw = envLoc->GetStringChars(string, &isCopy);
// todo add exception handling here for jvm
if (raw != NULL) {
value.assign(raw, raw + len);
converted = gcnew String(value.c_str());
env->ReleaseStringChars(string, raw);
}
}else{
TraceLog::Log("Using GetStringUTFChars() for string conversion.");
const char* raw = envLoc->GetStringUTFChars(string, &isCopy);
if(raw) {
int bufSize = MultiByteToWideChar(CP_UTF8, 0 , raw , -1, NULL , 0 );
wchar_t* wstr = new wchar_t[bufSize];
MultiByteToWideChar( CP_UTF8 , 0 , raw , -1, wstr , bufSize );
String^ val = gcnew String(wstr);
delete[] wstr;
converted = val; // partially working
envLoc->ReleaseStringUTFChars(string, raw);
}
}
}catch(Exception^ ex){
TraceLog::Log(ex->Message);
}
}
return converted;
}
Answer was to enable east asian languages in Windows XP as Win7 + Later work fine. Super easy.... waste of a entire day lol.
I'm trying to send some data from c++ to java using JNI.
In c++ I have:
Array[0]:
string name = "myName"
int iterations = 16
float value = 15
... etc
So I want to use JNI to return all data on Java, I'm trying this, but don't work
JNIEXPORT jobjectArray JNICALL Java_com_testing_data_MainActivity_getDATA(JNIEnv *env, jobject obj)
{
// 1º Create a temp object
jobject dataClass
{
jstring name;
jint iterations;
jfloat value;
};
jobject tempObject = env->NewObject();
// Get data in c++ format int temp object type
std::vector<dataClass > data = getDataClass(); // First error, must be a c++ class, how could i get it?
// How much memory i need?
int dataSize = data.size();
// Reserve memory in java format
jint tempValues[dataSize];
jobjectArray tempArray = env->NewObjectArray(dataSize,dataClass,0); // 2º Error, it doesn 't create the class
// Temporal store data in jarray
for (int i = 0; i < dataSize ; i++)
{
tempArray[i].name = data[i].name;
tempArray[i].iterations = data[i].iterations;
tempArray[i].value = data[i].value;
}
return tempArray; // return temp array
}
Are correct this steps to return a structure/object with data? How is possible to fix the errors?
Converting everything to JNI types is not a good idea. Generally, it is better to create a peer object, i.e. a handle pointer to the native resource - like the hWnd in the Windows GUI Programming.
You can use a string to store all data as a sequence data. Field are separated by a separator (":" for example), like this:
std::string sequenceData = "my name" + ":" + "16" + ":" + "15" + ...;
Pass this sequence to java then split it to get desired value. Use String.split() or StringTokenizer.
I have a purely native Android app, where the entry point is:
void android_main(struct android_app* state)
{
}
I need to access device information such as, android.os.Build.MODEL and in my android_main, have the following:
JNIEnv *env = state->activity->env;
jclass build_class = env->FindClass("android/os/Build");
jfieldID brand_id = env->GetStaticFieldID(build_class, "MODEL", "Ljava/lang/String;");
jstring brand_obj = (jstring)env->GetStaticObjectField(build_class, brand_id);
const char *nativeString = env->GetStringUTFChars(brand_obj, 0);
Unfortunately it crashes at the second line - "FindClass". I know that env is not null because I checked for this, but otherwise I am unable to debug this and find out why it is crashing :(
Turns out I had to:
state->activity->vm->AttachCurrentThread(&env, NULL);
instead of
JNIEnv *env = state->activity->env;
This post was helpful:
http://blog.tewdew.com/post/6852907694/using-jni-from-a-native-activity
Alternatively , you can do this in native code :-
#include <sys/system_properties.h>
#define ANDROID_OS_BUILD_MODEL "ro.product.model"
char model_id[PROP_VALUE_MAX]; // PROP_VALUE_MAX from <sys/system_properties.h>.
int len;
len = __system_property_get(ANDROID_OS_BUILD_MODEL, model_id);
// On return, len will equal (int)strlen(model_id).
model_id will contain the req info
Refer this for all constants