我正在尝试使用
Android NDK来从C中编写一些Java代码.该应用程序是一个NativeActivity应用程序.我必须访问一些仅在Java中可用的功能,并且该功能需要您将其他类子类化,所以我不能直接从C执行调用.因此,我有这样的Java代码:
// src/com/example/my/package/SubClass.java package com.example.my.package; import android.foo.TheSuperClass; public class SubClass extends TheSuperClass { public SubClass() { setImportantProperty(true); } }
我也有这样的C代码:
// Some file.c void setThatImportantJavaProperty() { JavaVM *vm = AndroidGetJavaVM(); // This returns a valid JavaVM object JNIEnv* env; (*vm)->AttachCurrentThread(vm,&env,0); jclass theSubClass = (*env)->FindClass(env,"com/example/my/package/SubClass"); jthrowable exception = (*env)->ExceptionOccurred(env); if (exception) { (*env)->ExceptionDescribe(env); // This gives me: "java.lang.NoClassDefFoundError: [generic]". // Also,theSubClass is null,so the next line causes a segfault. } jmethodID theSubClassConstructor = (*env)->GetMethodID(env,theSubClass,"<init>","()V"); jobject theSubClassObject = (*env)->NewObject(env,theSubClassConstructor); (*env)->DeleteLocalRef(env,theSubClass); (*env)->DeleteLocalRef(env,theSubClassConstructor); (*env)->DeleteLocalRef(env,theSubClassObject); (*vm)->DetachCurrentThread(vm); }
正如内嵌注释所说,运行这个给我一个“java.lang.NoClassDefFoundError:[generic]”错误.当我解压缩我的apk,它会显示我的classes.dex文件,它似乎有我的类.我的猜测是,有一些微妙的我在类路径上失踪,但是我无法解决这个问题.
顺便说一句,我可以从C中对C标准的Android库进行类似的调用,而不会出现问题(在上面的同一个C函数中).具体来说,我测试了调用Log.v,并且正确地打印输出.
看来我发现的所有例子都只显示了如何从C中调用普通的Java库,而不是自己编写的Java库,所以我甚至没有找到一个比较的示例项目.
解决方法
我的问题的微妙,以及为什么由Klaimmore和Winston链接的文档并不能解决这个问题,源自于我正在使用NativeActivity类编写一个应用程序.这意味着从来没有可以使用本地类加载器的Java堆栈.没有JNI_OnLoad调用,没有Java方法调用我的本机函数,没有其他方式(我知道)获取本地ClassLoader对象.不幸的是,绝大多数Android JNI文档根本就不是用NativeActivity编写的.
然而,有一个直接的解决方案,这个问题可以使您的生活更容易使用NativeActivity编写应用程序.简单地在Java中子类化NativeActivity.这允许您从NativeActivity的实例化的开始编写和访问任意的Java代码,同时仍然在C中执行NativeActivity允许的其他任何操作.它还允许您以这些文档中描述的方式从C设置您的JNI调用.这样做看起来像下面这样:
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(); } }
在你的C库中:
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; }
那么在C的某个时刻,你可以做:
// Some file.c void doSomethingAwesomeInJava() { JavaVM *vm = AndroidGetJavaVM(); // This returns a valid JavaVM object JNIEnv* env; (*vm)->AttachCurrentThread(vm,0); jmethodID myUsefulJavaFunction = (*env)->GetStaticMethodID(env,globalMyNativeActivityClass,"MyUsefulJavaFunction","()V"); (*env)->CallStaticVoidMethod(env,theActivityClass,myUsefulJavaFunction); (*env)->DeleteLocalRef(env,myUsefulJavaFunction); (*vm)->DetachCurrentThread(vm); }
那就是我发现使用NativeActivity应用程序并入我自己的新Java类的方式.希望这将对我以外的人有用.