开发者

Java calling C calling Java

开发者 https://www.devze.com 2023-02-19 12:59 出处:网络
I want to call a C funcion from Java using JNI. And in the C function, I want to create a JVM and call some Java objects. When I try to create the JVM, JNI_CreateJavaVM returns -1.

I want to call a C funcion from Java using JNI. And in the C function, I want to create a JVM and call some Java objects. When I try to create the JVM, JNI_CreateJavaVM returns -1.

So,开发者_开发问答 I want to know if it is possible to do this. The C code is compiled to create a .so file (in linux), and the Java code calls the function in the .so file.

Any example doing Java->C->Java will be useful.

Thanks.


No, unfortunately its not possible. You can only have one JVM per process, and you're already in a JVM process.


I can not see the point in that Java-->C-->Java.

If you can call C from Java, then you can call Java from Java after your C function returns.

Excelsion xFunction is an easy and reliable library for calling C from Java. It handles the weird JNI stuff giving you a simpler interface.


Why do you need to create a second JVM? You can't create a second JVM, but you can access Java classes from C code. See Accessing Java Objects.


I can't see why some people consider this useless. In fact, the Java -> C -> Java scenario is very common when you get into Android game programming:

  1. The Android app starts from some boilerplate code written in Java (using the SDK)
  2. A new thread is started to run the main logic of the game, which is written in C / C++ for efficiency (and the easier access to OpenGL ES)
  3. The C / C++ code calls some Java methods for the required functionalities

The reason why it is necessary to use the SDK (Java) is that most of the Android APIs are provided only in Java! The NDK (C / C++) only provides a slim Linux environment (think it as "Standard C Library" + "Standard C++ Library" + "Cutdown Version of Linux System Libraries"). For example, if you want to integrate your app with AdMob (Google Ads), or In-app Billing (Google Payment), or if you want to access the built-in camera of the device, you have to invoke Java methods from the game logic (which is written is C / C++!).

The Java -> C Part

Write a Java class. Declare one method 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");
    }
}

Invoke the "Java compiler" (javac), followed by the "C header and stub file generator" (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

Implement the C part:

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

Compile the C source (Windows):

> REM TODO: Add this!

Compile the C source (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/

Compile the C source (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/

Run the Java application:

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

The C -> Java Part

Since you started your program in Java (instead of in C / C++), a JVM is already created. You don't need to create another one in the second part (C -> Java part), you just need to reuse the one already created. There are 2 ways to do it:

  1. By using the Java Environment pointer (JNIEnv*) provided by the JNI call from the first part (Java -> C part)
  2. By using the Java Virtual Machine pointer (JavaVM*) saved during JNI_OnLoad()

(The answer is not finished and I will finish it later. If you like it, please vote me up for encouragement. Thanks!)

References:

  • JNI Functions - Java Native Interface Specification
  • The Invocation API - Java Native Interface Specification
  • JNI Tips | Android Developers
0

精彩评论

暂无评论...
验证码 换一张
取 消