Java 调用 C,C 调用 Java

5
我希望使用JNI从Java调用C函数。在C函数中,我想创建一个JVM并调用一些Java对象。当我尝试创建JVM时,JNI_CreateJavaVM返回-1。
因此,我想知道是否可能实现这个目标。C代码编译后生成.so文件(在Linux上),Java代码调用.so文件中的函数。
任何做Java->C->Java的示例都将非常有用。
谢谢。
4个回答

2
抱歉,这是不可能的。每个进程只能有一个JVM,并且你已经在一个JVM进程中了。

1
不是必须的,这取决于你想做什么。正如其他帖子所说,你可以通过C JNI函数从你的进程访问Java对象。 - MarcB

2

我对Java->C->Java的操作没有看到意义。

如果你可以从Java调用C,那么在C函数返回后,你也可以从Java调用Java。

Excelsion xFunction是一个易于使用和可靠的库,用于从Java中调用C。它处理了奇怪的JNI内容并为您提供了更简单的接口。


1
有时候这样的功能是很有用的。但我并不认为回调到外部虚拟机是一个好主意。 - biziclop
在某些情况下,这是非常有用的。例如,您可以使用C代码创建一个线程(Java-->C),然后使用C-->Java进行回调。 - fbafelipe

2
为什么需要创建第二个JVM?你无法创建第二个JVM,但可以从C代码中访问Java类。请参见访问Java对象

1

我不明白为什么有些人认为这是无用的。事实上,当你涉足Android游戏编程时,Java->C->Java的情况非常普遍:

  1. Android应用程序从使用SDK编写的一些样板代码开始(使用Java)
  2. 启动一个新线程来运行游戏的主要逻辑,该逻辑使用C/C++编写以提高效率(并更容易访问OpenGL ES
  3. C/C++代码调用一些Java方法以获取所需的功能
使用 SDK(Java) 的原因是大多数 Android APIs 只提供 Java!NDK(C/C++) 仅提供精简的 Linux 环境(将其视为“标准 C 库”+“标准 C++ 库”+“Linux 系统库的削减版”)。例如,如果您想将应用与 AdMob(Google 广告)In-app Billing(Google 支付) 集成,或者想要访问设备的内置相机,则必须从游戏逻辑(编写为 C/C ++!)调用 Java 方法。

Java -> C 部分

编写一个 Java 类。声明一个方法 native:

$ ls -F1
classes/
jni/
src/

$ nano src/com/example/jcj/Test.java

Test.java:

package com.example.jcj;

public class Test {
    public static void main(String[] args) {
        doSomethingInC();
    }

    public static native void doSomethingInC();

    public static void doSomethingInJava() {
        System.out.println("Done something in Java!");
    }

    static {
        System.loadLibrary("hello_jcj");
    }
}

调用“Java编译器”(javac),然后是“C头文件和存根文件生成器”(javah):
$ javac -sourcepath src -d classes src/com/example/jcj/Test.java

$ javah -classpath classes -d jni com.example.jcj.Test

$ ls -F1 jni/
com_example_jcj_Test.h

实现 C 部分:

$ nano jni/com_example_jcj_Test.c

com_example_jcj_Test.c:

#include "com_example_jcj_Test.h"
#include <stdio.h>

/*
 * Class:     com_example_jcj_Test
 * Method:    doSomethingInC
 * Signature: ()V
 */
JNIEXPORT void JNICALL
Java_com_example_jcj_Test_doSomethingInC(JNIEnv* env, jclass clazz)
{
    printf("Done something in C!\n");
}

编译C源代码(Windows):

> REM TODO: Add this!

编译C源代码(Linux):

$ gcc -I{JAVA_HOME}/include
      -shared \
      -o libhello_jcj.so \
      jni/com_example_jcj_Test.c

$ ls -F1
classes/
jni/
libhello_jcj.so*
src/

编译C源代码(Mac OS X):

$ gcc -I${JAVA_HOME}/include \
      -shared \
      -o libhello_jcj.jnilib \
      jni/com_example_jcj_Test.c

$ ls -F1
classes/
jni/
libhello_jcj.jnilib*
src/

运行Java应用程序:

$ java -classpath classes com.example.jcj.Test
Done something in C!

C -> Java 部分

由于您在 Java 中启动了程序(而不是在 C / C++ 中),因此 JVM 已经创建。在第二部分(C -> Java 部分)中,您无需创建另一个 JVM,只需重用已创建的 JVM 即可。有两种方法可以实现:

  1. 通过使用 JNI 调用从第一部分(Java -> C 部分)提供的 Java 环境指针 (JNIEnv*)。
  2. 通过在 JNI_OnLoad() 期间保存的 Java 虚拟机指针 (JavaVM*)。

(答案尚未完成,稍后将继续更新。如果您喜欢这篇翻译,请点赞以示鼓励。谢谢!)

参考资料:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接