Friday, November 2, 2012

Calling Java function from C

How do we call a Java function from a C Program ?

Here is a program that shows to do it

Contents of MainClass.java : We will be calling "main" from the C-program

public class MainClass {

    public static void print() {
        System.out.println("\n This is called inside the java program \n");
    }

    public static void main(String[] args) {
        System.out.println("[ test program ]");

        print();

        return;
    }

}

Next, compile the Java program

# javac MainClass.java

Next, we write a C program ("test.c" below) which will call the above "main" function

#include <jni.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
#include <time.h>
#include <stdlib.h>

JNIEnv* create_vm()
{
    JavaVM *jvm;
    JNIEnv *env;
    JavaVMInitArgs vm_args;

    JavaVMOption options[1];

    options[0].optionString = "-Djava.class.path=/home/giri";

    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 1;
    vm_args.options = &options;
    vm_args.ignoreUnrecognized = JNI_FALSE;

    JNI_CreateJavaVM(&jvm, (void **) &env, &vm_args);

    return env;
}

void invoke_class(JNIEnv *env)
{
    jclass helloWorldClass;
    jmethodID mainMethod;
    jobjectArray applicationArgs;
    jstring applicationArg0;

    helloWorldClass = (*env)->FindClass(env, "MainClass");
    mainMethod = (*env)->GetStaticMethodID(env, helloWorldClass, "main", "([Ljava/lang/String;)V");

    applicationArgs = (*env)->NewObjectArray(env, 1, (*env)->FindClass(env, "java/lang/String"), NULL);

    applicationArg0 = (*env)->NewStringUTF(env, "From-C-Program");

    (*env)->SetObjectArrayElement(env, applicationArgs, 0, applicationArg0);

    (*env)->CallStaticVoidMethod(env, helloWorldClass, mainMethod, applicationArgs);

}

int main()
{
    JNIEnv *env = create_vm();
    invoke_class(env);

    return 0;
}

Next step is to compile the C program

# gcc test.c -o TEST -I/usr/lib/jvm/java-7-openjdk-amd64/include -L/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/amd64/server -ljvm

Now, we run the program !

giri@giri-vm:~$ ./TEST 
[ test program ]

 This is called inside the java program 

giri@giri-vm:~$

How to get correct function signatures ?


$ javap -s -p MainClass

Warning: Binary file MainClass contains com.giri.jni.MainClass
Compiled from "MainClass.java"
public class com.giri.jni.MainClass {
  public com.giri.jni.MainClass();
    Signature: ()V

  public native java.lang.String nativeFoo();
    Signature: ()Ljava/lang/String;

  public void print();
    Signature: ()V

  public static void main(java.lang.String[]);
    Signature: ([Ljava/lang/String;)V

  static {};
    Signature: ()V
}

5 comments :

  1. Hi,

    First, thanks for your post!

    I need call from a pure c method to a method of a Java class and i've used his code.

    I've included the c methods in picture.c:
    JNIEnv* create_vm(){
    JavaVM *jvm;
    JNIEnv *env;
    JavaVMInitArgs vm_args;

    JavaVMOption options[1];

    options[0].optionString = "-Djava.class.path=/home/virginia/workspace/android/vlc-android/src/";

    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 1;
    vm_args.options = &options;
    //vm_args.ignoreUnrecognized = JNI_FALSE;

    JNI_CreateJavaVM(&jvm, (void **) &env, &vm_args);

    return env;
    }

    void invoke_class(JNIEnv *env){
    jclass helloWorldClass;
    jmethodID mainMethod;
    jobjectArray applicationArgs;
    jstring applicationArg0;

    helloWorldClass = (*env)->FindClass(env, "VirtualActivity");
    mainMethod = (*env)->GetStaticMethodID(env, helloWorldClass, "main", "([Ljava/lang/String;)V");

    applicationArgs = (*env)->NewObjectArray(env, 1, (*env)->FindClass(env, "java/lang/String"), NULL);

    applicationArg0 = (*env)->NewStringUTF(env, "From-C-Program");

    (*env)->SetObjectArrayElement(env, applicationArgs, 0, applicationArg0);

    (*env)->CallStaticVoidMethod(env, helloWorldClass, mainMethod, applicationArgs);

    }

    int calling_c_java(){
    JNIEnv *env = create_vm();
    invoke_class(env);

    return 0;
    }


    And the Java methods in VirtualActivity.java.

    public static void print() {
    System.out.println("\n This is called inside the java program \n");
    }

    public static void main(String[] args) {
    System.out.println("[ test program ]");

    print();

    return;
    }


    In the Android.mk file, i've written the next lines:

    include $(CLEAR_VARS)

    LOCAL_MODULE := native
    LOCAL_SRC_FILES := ../../vlc/src/misc/picture.c


    LOCAL_C_INCLUDES := \
    -I/usr/lib/jvm/jvm/java-7-openjdk-amd64/include \
    -I/usr/lib/jvm/jvm/java-7-openjdk-amd64/include/linux \

    LOCAL_LDLIBS := -L/usr/lib/jvm/java-7-openjdk-amd64/jre/lib \
    -L/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/amd64 -ljava -lverify \
    -L/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/amd64/server -ljvm \

    include $(BUILD_SHARED_LIBRARY)

    When i compile, i get the next error:
    /home/vmg/android/android-ndk-r8c/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ../vlc/android/src/.libs/libvlccore.a(picture.o): in function create_vm:../../src/misc/picture.c:683: error: undefined reference to 'JNI_CreateJavaVM'

    What am i doing wrong?
    Thanks so much!

    ReplyDelete
  2. Thanks so much. It's very helpful

    ReplyDelete
  3. Hi,

    Thanks for the post :-)

    When i try running ./TEST i get the following error :

    Problematic frame:
    # V [libjvm.so+0x49d341] get_method_id(JNIEnv_*, _jclass*, char const*, char const*, bool, Thread*) [clone .isra.81]+0x81

    I know C, but have no idea about java!
    Please help me!


    Thanks in advance!

    ReplyDelete
    Replies
    1. Hi,

      Issue solved :-)
      I was giving wrong path in options[0].optionString.

      Once again, Thanks for the post !!

      Delete
  4. Thanks much for this very clear post.. I will be trying it out

    ReplyDelete