I am trying to call some Java code that I wrote from C using the Android NDK. The application is a NativeActivity application. I have to access some functionality that is
The subtlety of my question, and why the documentation linked to by Klaimmore and Winston does not quite resolve the issue, stems from the fact that I am writing an app using the NativeActivity class. This means that there is never a Java stack with a local class loader available to me. There is no JNI_OnLoad call, there is no Java method calling into my native function, and there is no other way (that I am aware of) of getting ahold of a local ClassLoader object. Unfortunately, most of the Android JNI documentation is simply not written with NativeActivity in mind.
However, there is a straightforward solution to this problem that can make your life much easier when writing apps using NativeActivity. Simply subclass NativeActivity in Java. This allows you to write and access arbitrary Java code from the beginning of your NativeActivity's instantiation, while still doing everything else in C as NativeActivity allows. It also allows you to set up your JNI calls from C in the manner described in those documents. Doing this looks something like the following:
package com.example.my.package;
import android.app.NativeActivity;
import android.util.Log;
public class MyNativeActivity extends NativeActivity {
static {
System.loadLibrary("my_ndk_lib");
}
private static String TAG = "MyNativeActivity";
public MyNativeActivity() {
super();
Log.v(TAG, "Creating MyNativeActivity");
}
public static void MyUsefulJavaFunction() {
doSomethingAwesome();
}
}
And in your C library:
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK)
return -1;
globalMyNativeActivityClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "com/example/my/package/MyNativeActivity"));
return JNI_VERSION_1_6;
}
Then at some point in C, you can do:
// Some file.c
void doSomethingAwesomeInJava() {
JavaVM *vm = AndroidGetJavaVM(); // This returns a valid JavaVM object
JNIEnv* env;
(*vm)->AttachCurrentThread(vm, &env, 0);
jmethodID myUsefulJavaFunction = (*env)->GetStaticMethodID(env, globalMyNativeActivityClass, "MyUsefulJavaFunction", "()V");
(*env)->CallStaticVoidMethod(env, theActivityClass, myUsefulJavaFunction);
(*env)->DeleteLocalRef(env, myUsefulJavaFunction);
(*vm)->DetachCurrentThread(vm);
}
And that is the way I found to incorporate my own new Java classes with a NativeActivity app. Hopefully this will be useful to someone besides me.