我正在尝试使用
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类的方式.希望这将对我以外的人有用.