I can't see why some people consider this useless. In fact, the Java -> C -> Java scenario is very common when you get into Android game programming:
- The Android app starts from some boilerplate code written in Java (using the SDK)
- A new thread is started to run the main logic of the game, which is written in C / C++ for efficiency (and the easier access to OpenGL ES)
- The C / C++ code calls some Java methods for the required functionalities
The reason why it is necessary to use the SDK (Java) is that most of the Android APIs are provided only in Java! The NDK (C / C++) only provides a slim Linux environment (think it as "Standard C Library" + "Standard C++ Library" + "Cutdown Version of Linux System Libraries"). For example, if you want to integrate your app with AdMob (Google Ads), or In-app Billing (Google Payment), or if you want to access the built-in camera of the device, you have to invoke Java methods from the game logic (which is written is C / C++!).
The Java -> C Part
Write a Java class. Declare one method native:
$ ls -F1
classes/
jni/
src/
$ nano src/com/example/jcj/Test.java
Test.java:
package com.example.jcj;
public class Test {
public static void main(String[] args) {
doSomethingInC();
}
public static native void doSomethingInC();
public static void doSomethingInJava() {
System.out.println("Done something in Java!");
}
static {
System.loadLibrary("hello_jcj");
}
}
Invoke the "Java compiler" (javac), followed by the "C header and stub file generator" (javah):
$ javac -sourcepath src -d classes src/com/example/jcj/Test.java
$ javah -classpath classes -d jni com.example.jcj.Test
$ ls -F1 jni/
com_example_jcj_Test.h
Implement the C part:
$ nano jni/com_example_jcj_Test.c
com_example_jcj_Test.c:
#include "com_example_jcj_Test.h"
#include <stdio.h>
/*
* Class: com_example_jcj_Test
* Method: doSomethingInC
* Signature: ()V
*/
JNIEXPORT void JNICALL
Java_com_example_jcj_Test_doSomethingInC(JNIEnv* env, jclass clazz)
{
printf("Done something in C!\n");
}
Compile the C source (Windows):
> REM TODO: Add this!
Compile the C source (Linux):
$ gcc -I{JAVA_HOME}/include
-shared \
-o libhello_jcj.so \
jni/com_example_jcj_Test.c
$ ls -F1
classes/
jni/
libhello_jcj.so*
src/
Compile the C source (Mac OS X):
$ gcc -I${JAVA_HOME}/include \
-shared \
-o libhello_jcj.jnilib \
jni/com_example_jcj_Test.c
$ ls -F1
classes/
jni/
libhello_jcj.jnilib*
src/
Run the Java application:
$ java -classpath classes com.example.jcj.Test
Done something in C!
The C -> Java Part
Since you started your program in Java (instead of in C / C++), a JVM is already created. You don't need to create another one in the second part (C -> Java part), you just need to reuse the one already created. There are 2 ways to do it:
- By using the Java Environment pointer (
JNIEnv*) provided by the JNI call from the first part (Java -> C part)
- By using the Java Virtual Machine pointer (
JavaVM*) saved during JNI_OnLoad()
(The answer is not finished and I will finish it later. If you like it, please vote me up for encouragement. Thanks!)
References: