itf*_*che 10 c java java-native-interface android android-ndk
我试图调用我使用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);
}
}
Run Code Online (Sandbox Code Playgroud)
我也有这样的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, theSubClass, theSubClassConstructor);
(*env)->DeleteLocalRef(env, theSubClass);
(*env)->DeleteLocalRef(env, theSubClassConstructor);
(*env)->DeleteLocalRef(env, theSubClassObject);
(*vm)->DetachCurrentThread(vm);
}
Run Code Online (Sandbox Code Playgroud)
正如内联注释所说,运行它会给我一个"java.lang.NoClassDefFoundError:[generic]"错误.当我解压缩我的apk时,它会显示classes.dex文件,它似乎包含了我的类.我的猜测是有一些关于类路径的遗漏,但到目前为止我无法解决它.
顺便说一句,我能够从C中对标准的Android库进行类似的调用而没有问题(在上面的相同C函数中).具体来说,我测试了调用Log.v并且正常工作并打印输出.
似乎我发现的所有示例都只显示如何从C调用普通的Java库,而不是你自己编写的Java库,所以我甚至没有找到一个可以比较的示例项目.
itf*_*che 12
我的问题的微妙之处,以及为什么由Klaimmore和Winston链接的文档并没有完全解决问题,这源于我使用NativeActivity类编写应用程序的事实.这意味着永远不会有一个带有本地类加载器的Java堆栈.没有JNI_OnLoad调用,没有Java方法调用我的本机函数,并且没有其他方法(我知道)获取本地ClassLoader对象的ahold.不幸的是,大多数Android JNI文档都不是用NativeActivity编写的.
但是,这个问题有一个直接的解决方案,可以让您在使用NativeActivity编写应用程序时更轻松.简单地在Java中继承NativeActivity.这允许您从NativeActivity的实例化开始时编写和访问任意Java代码,同时仍然在NativeActivity允许的C中执行其他所有操作.它还允许您以这些文档中描述的方式从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();
}
}
Run Code Online (Sandbox Code Playgroud)
在你的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;
}
Run Code Online (Sandbox Code Playgroud)
然后在C的某个时刻,你可以这样做:
// 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);
}
Run Code Online (Sandbox Code Playgroud)
这就是我发现将自己的新Java类与NativeActivity应用程序结合在一起的方式.希望这对我以外的人有用.
这是我从克莱莫尔提到的这个链接中摘录的内容。
有几种方法可以解决这个问题:
在 JNI_OnLoad 中进行一次 FindClass 查找,并缓存类引用以供以后使用。作为执行 JNI_OnLoad 的一部分进行的任何 FindClass 调用都将使用与调用 System.loadLibrary 的函数关联的类加载器(这是一个特殊规则,旨在使库初始化更加方便)。如果您的应用程序代码正在加载库,FindClass 将使用正确的类加载器。*
通过声明本机方法接受 Class 参数,然后传入 Foo.class,将类的实例传递到需要它的函数中。
将 ClassLoader 对象的引用缓存在方便的地方,然后直接发出 loadClass 调用。这需要一些努力。
| 归档时间: |
|
| 查看次数: |
10054 次 |
| 最近记录: |