如何在 Android 上的 C 中加载我自己的 Java 类? [英] How do I load my own Java class in C on Android?

查看:18
本文介绍了如何在 Android 上的 C 中加载我自己的 Java 类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试调用我使用 Android NDK 从 C 编写的一些 Java 代码.该应用程序是一个 NativeActivity 应用程序.我必须访问一些仅在 Java 中可用的功能,并且该功能需要您对另一个类进行子类化,所以我不能直接从 C 中进行调用.因此,我有这样的 Java 代码:

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 only available in Java, and the functionality requires you to subclass another class, so I can't just directly do the calls from C. Thus, I have Java code like this:

// 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 代码:

I also have C code like this:

// 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);

}

正如内联注释所说,运行它会给我一个java.lang.NoClassDefFoundError: [generic]"错误.当我解压缩我的 apk 时,它显示了 classes.dex 文件,其中似乎包含我的课程.我的猜测是我在类路径方面缺少一些微妙之处,但到目前为止我无法解决它.

As the inline comments say, running this gives me a "java.lang.NoClassDefFoundError: [generic]" error. When I unpack my apk, it shows me the classes.dex file, which appears to have my class in it. My guess is that there is some subtlety that I am missing regarding classpaths, but I am unable to resolve it thus far.

顺便说一句,我可以毫无问题地从 C 对标准 Android 库进行类似调用(在上面的相同 C 函数中).具体来说,我测试了调用 Log.v,它可以正常工作并正确打印输出.

Incidentally, I am able to make similar calls to standard Android libraries from C without a problem (in the same C function above). Specifically, I tested calling Log.v and that works and prints the output correctly.

似乎我找到的所有示例都只显示了如何从 C 调用普通 Java 库,而不是您自己编写的 Java 库,所以我什至没有找到可以比较的示例项目.

It seems that all the examples that I am finding only show how to call normal Java libraries from C, not Java libraries you have written yourself, so I haven't even found an example project to compare against.

推荐答案

我的问题的微妙之处,以及为什么 Klaimmore 和 Winston 链接的文档不能完全解决问题,源于我正在编写一个应用程序这一事实使用 NativeActivity 类.这意味着我永远不会使用带有本地类加载器的 Java 堆栈.没有 JNI_OnLoad 调用,没有 Java 方法调用我的本机函数,也没有其他方法(据我所知)来获取本地 ClassLoader 对象.不幸的是,大多数 Android JNI 文档根本就没有考虑到 NativeActivity.

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.

但是,对于这个问题,有一个直接的解决方案可以让您在使用 NativeActivity 编写应用程序时更轻松.只需在 Java 中继承 NativeActivity.这允许您从 NativeActivity 的实例化开始编写和访问任意 Java 代码,同时仍然可以在 NativeActivity 允许的情况下在 C 中执行其他所有操作.它还允许您以这些文档中描述的方式从 C 中设置 JNI 调用.这样做看起来如下所示:

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();
  }
}

在你的 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 中的某个时刻,你可以这样做:

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);
}

这就是我发现将我自己的新 Java 类与 NativeActivity 应用程序结合起来的方式.希望这对我以外的人有用.

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.

这篇关于如何在 Android 上的 C 中加载我自己的 Java 类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆