Call C++ functions from Java in cocos2d-x
31 Aug 2014If you’ve read Part 1. Calling Java functions from C++ in cocos2d-x, you’ll agree with me that the task is not as hard as it seems. Fortunately, the other way around, i.e., calling C++ functions from Java, is also quite straight forward.
The following note actually applies for all native Android applications, not just cocos2d-x games.
Simplest case: void method with no arguments
There’re 2 steps to make a C++ function callable in Java code:
- Declare a
native
method in Java code - Name the C++ function follows a rule
1. Declare a native
method in Java code
Suppose there’s a awesomeCppFunction()
you wanna call from Java, first you’ll need to declare it as native
method in your Java class:
public static native void awesomeCppFunction();
Calling this method in Java is as normal as calling any ordinary Java method:
package com.myhouse;
class HappyJavaClass {
public static native void awesomeCppFunction();
private void myOrdinaryJavaMethod() {
String a = "Yes, it's pure Java!";
awesomeCppFunction();
}
}
2. Name the C++ function follows a rule
C++ function name must contain Java full-path classname which declared its signature in step (1).
In the above example, your C++ function must be named Java_com_myhouse_HappyJavaClass_awesomeCppFunction
.
It must be declared in .cpp
file like so:
extern "C"
{
JNIEXPORT void JNICALL Java_com_myhouse_HappyJavaClass_awesomeCppFunction(JNIEnv* env, jobject thiz);
};
JNIEXPORT void JNICALL Java_com_myhouse_HappyJavaClass_awesomeCppFunction(JNIEnv* env, jobject thiz)
{
// your C++ code goes here
}
The first 2 arguments JNIEnv* env, jobject thiz
are required and are used to extract data sent from Java to C++.
Pass parameters and return value
On Java side, you declare method params and return type as usual:
public static native String awesomeCppFunction(int x, boolean y, String z);
On C++ side, you’ll need to “convert” Java types to JNI (native) types.
JNI Types | Java Type |
---|---|
void | void |
jboolean | boolean |
jbyte | byte |
jchar | char |
jshort | short |
jint | int |
jlong | long |
jfloat | float |
jdouble | double |
jobject | All Java objects |
jclass | java.lang.Class objects |
jstring | java.lang.String objects |
jobjectArray | Array of objects |
jbooleanArray | Array of booleans |
jbyteArray | Array of bytes |
jshortArray | Array of shorts |
jintArray | Array of integers |
jlongArray | Array of longs |
jfloatArray | Array of floats |
jdoubleArray | Array of doubles |
Add parameters after the 2 required params:
extern "C"
{
JNIEXPORT jstring JNICALL Java_com_myhouse_HappyJavaClass_awesomeCppFunction(JNIEnv* env, jobject thiz, jint myIntParam, jstring myStringParam);
};
JNIEXPORT jstring JNICALL Java_com_myhouse_HappyJavaClass_awesomeCppFunction(JNIEnv* env, jobject thiz, jint myIntParam, jstring myStringParam)
{
// your C++ code goes here
}
Use primitive type parameters
Primitive data (e.g., jboolean, jint, jchar, jfloat…) can be used as normal C++ equivalent data types:
JNIEXPORT void JNICALL Java_com_myhouse_HappyJavaClass_awesomeCppFunction(JNIEnv* env, jobject thiz, jint myIntParam)
{
int myCppVariable = myIntParam;
}
Use object parameters
Objects passed as parameters need to be converted to C++ equivalent data types:
Examples:
String
JNIEXPORT void JNICALL Java_com_myhouse_HappyJavaClass_awesomeCppFunction(JNIEnv* env, jobject thiz, jstring myStringParam)
{
const char* str = env->GetStringUTFChars(myStringParam, NULL);
}
Array of integers:
Example from here:
JNIEXPORT void JNICALL Java_Simple_passArray(JNIEnv *env, jobject obj, jintArray ptr)
{
int i;
jsize len = env->GetArrayLength(env, ptr);
jint *body = env->GetIntArrayElements(env, ptr, 0);
for (i=0; i < len; i++)
printf("Hello from JNI - element: %d\n", body[i]);
env->ReleaseIntArrayElements(env, ptr, body, 0);
}
Array of Strings:
Example from here:
JNIEXPORT void JNICALL Java_Simple_passArray(JNIEnv *env, jobject obj, jstringArray ptr)
{
int i;
jsize len = env->GetArrayLength(env, ptr);
jstring *body = env->GetObjectArrayElement(env, ptr, 0);
for (i=0; i < len; i++)
{
const char *rawString = env->GetStringUTFChars(body[i], false);
printf("Hello from JNI - element: %s\n", rawString);
env->ReleaseStringUTFChars(body[i], rawString);
}
env->ReleaseObjectArrayElements(env, ptr, body, 0);
}
Return primitive data
Returning primitive data (e.g., jboolean, jint, jchar, jfloat…) is just as you normally do in C++:
JNIEXPORT jint JNICALL Java_com_myhouse_HappyJavaClass_awesomeCppFunction(JNIEnv* env, jobject thiz)
{
int i = 3;
return i;
}
Return object
To return an object, you’ll need to convert your C++ object to equivalent JNI object.
Examples:
String
JNIEXPORT jstring JNICALL Java_com_myhouse_HappyJavaClass_awesomeCppFunction(JNIEnv* env, jobject thiz)
{
jstring result = env->NewStringUTF(FacebookManager::GetInstance()->GetWhitelistDevIds());
env->DeleteLocalRef(result);
return result;
}
Array of integers
Example extracted from this stackoverflow answer:
JNIEXPORT jintArray JNICALL Java_com_myhouse_HappyJavaClass_awesomeCppFunction(JNIEnv* env, jobject thiz)
{
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[256];
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;
}
Array of Strings
Example extracted from this stackoverflow answer:
JNIEXPORT jobjectArray JNICALL Java_com_myhouse_HappyJavaClass_awesomeCppFunction(JNIEnv* env, jobject thiz)
{
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);
}
That’s it! Happy coding :D
Comments
comments powered by Disqus