Game Development Reference
In-Depth Information
The log.h header defines a function called __android_log_print , which is similar to the standard
C printf function. It takes a log level and two char* pointers representing the tag and the
message. Our tag and message parameters have the type jstring and can't be cast to char*
pointers. Instead, we have to temporarily convert them to char* pointers via methods exposed
by the env parameter. This is done in the first two lines of the function via calls to
env->GetStringUTFChars() .
Next, we simply call the logging method, passing in the parameters. Finally, we need to clean up
the converted strings so that we don't leak memory. This is done via
env->ReleaseStringUTFChars() .
JNIEXPORT void JNICALL Java_com_badlogic_androidgames_ndk_JniUtils_copy
(JNIEnv *env, jclass clazz, jobject dst, jfloatArray src, jint offset, jint len) {
unsigned char* pDst = ( unsigned char* )env->GetDirectBufferAddress(dst);
float* pSrc = ( float* )env->GetPrimitiveArrayCritical(src, 0);
memcpy(pDst, pSrc+offset, len * 4);
env->ReleasePrimitiveArrayCritical(src, pSrc, 0);
}
The second function takes a direct ByteBuffer , a float array, an offset into the float array, and the
number of floats we want to copy. Note that the ByteBuffer has the type jobject ! Whenever you
pass in anything other than a primitive type or an array, you will get a jobject . Your
C/C++ code needs to know which type to expect! In our case, we know that we are getting a
ByteBuffer instance. ByteBuffer instances are just thin wrappers around a native memory area.
They are super easy to handle in C; we can simply fetch a pointer to their memory address via
env->GetDirectBufferAddress() .
Our float array is a bit more difficult to handle. The env->GetPrimitiveArrayCritical() method
will lock the array and return a pointer to its first element. Using this function is dangerous; you
should not try to concurrently modify the array in Java. Calling any other JNI method from this
point on is prohibited as well. Otherwise, you'll get hard-to-debug behaviors in your C/C++ code!
Once we have the pointers, we simply use memcpy() to copy the contents of the float array to the
ByteBuffer . Note that we do not perform any kind of bounds checking, which means the Java
code calling this method must be bulletproof. Trying to copy more floats into the ByteBuffer
than we allocated might result in a nasty segmentation fault. The same is true when we specify
offsets and lengths that are outside of the float array we passed in. The general takeaway is that
you have to know what you are doing when using JNI and the NDK. If you don't, your application
will just explode with hard-to-debug errors!
At the end of the function, we unlock the float array again via a call to
env->ReleasePrimitiveArrayCritical() . You must call this method under all circumstances;
otherwise you'll run into all kinds of issues.
With our C/C++ header and source files in place, it's time to build the shared library.
Building the Shared Library
As mentioned earlier, the NDK comes with its own build system. While it still uses standard
makefiles under the hood, you as a user don't have to deal with their complexity. Instead, you
 
Search WWH ::




Custom Search