2010年10月4日 星期一

JNI - Access Instance (object) / Static (class) Method

There are several kinds of methods in the Java programming language.
  • (Object) Instance methods must be invoked on a specific instance of a class.
  • (Class) Static methods may be invoked independent of any instance.
  • Calling Instance Methods
  • (Example) InstanceMethodCall.java: a native method that in turn calls an instance method implemented in the Java programming language.
    1. InstanceMethodCall.java
    2. class InstanceMethodCall {
          private native void nativeMethod();
          private void callback() {
              System.out.println("Output: In Java code");
          }
          public static void main(String args[]) {
              InstanceMethodCall c = new InstanceMethodCall();
              c.nativeMethod();
          }
          static {
              System.loadLibrary("InstanceMethodCall");
          }
      }
      
    3. Generate C header file: InstanceMethodCall.h
    4. /* DO NOT EDIT THIS FILE - it is machine generated */                                                    
      #include <jni.h>
      /* Header for class InstanceMethodCall */
      
      #ifndef _Included_InstanceMethodCall
      #define _Included_InstanceMethodCall
      #ifdef __cplusplus
      extern "C" {
      #endif
      /*
       * Class:     InstanceMethodCall
       * Method:    nativeMethod
       * Signature: ()V
       */
      JNIEXPORT void JNICALL Java_InstanceMethodCall_nativeMethod
        (JNIEnv *, jobject);
      
      #ifdef __cplusplus
      }
      #endif
      #endif
      
    5. C implementation of InstanceMethodCall.c
    6. #include <stdio.h>
      #include <jni.h>
      
      JNIEXPORT void JNICALL 
       Java_InstanceMethodCall_nativeMethod(JNIEnv *env, jobject obj)
       {
           jclass cls = (*env)->GetObjectClass(env, obj);
           jmethodID mid = 
               (*env)->GetMethodID(env, cls, "callback", "()V");
           if (mid == NULL) {
               return; /* method not found */
           }
           printf("Output: In C code\n");
           (*env)->CallVoidMethod(env, obj, mid);
       }
      
        Two steps required to call an instance method:
      1. GetMethodID performs a lookup for the method in the given class. The lookup is based on the name and type descriptor of the method. If the method does not exist, GetMethodID returns NULL and causes a NoSuchMethodError to be thrown in the code.
      2. CallVoidMethod invokes an instance method that has the return type void. You pass the object, the method ID, and the actual arguments to CallVoidMethod.
    7. Generate the shared library file - libInstanceMethodCall.so
    8. gcc -shared -I/usr/local/jdk1.6.20_21/include -I/usr/local/jdk1.6.20_21/include/linux InstanceMethodCall.c -o libInstanceMethodCall.so
    9. RUN InstanceMethodCall output:
    10. Output: In C code
      Output: In Java code

  • Calling Static Methods
  • Similarly, we can perform callbacks to static methods from native code by following steps:
    • Obtain the method ID using GetStaticMethodID, as opposed to GetMethodID.
    • Pass the class, method ID, and arguments to one of the family of static method invocation functions: CallStaticVoidMethod, CallStaticBooleanMethod, and so on.
      (Example) StaticMethodCall.java: a slight variation of the earlier InstanceMethodCall example
    1. StaticMethodCall.java
    2. class StaticMethodCall {
          private native void nativeMethod();
          private static void callback() {
              System.out.println("Output: In Java code");
          }
          public static void main(String args[]) {
              StaticMethodCall c = new StaticMethodCall();
              c.nativeMethod();
          }
          static {
              System.loadLibrary("StaticMethodCall");
          }
      }
      
    3. Generate C header file: StaticMethodCall.h
    4. /* DO NOT EDIT THIS FILE - it is machine generated */                           
      #include <jni.h>
      /* Header for class StaticMethodCall */
      
      #ifndef _Included_StaticMethodCall
      #define _Included_StaticMethodCall
      #ifdef __cplusplus
      extern "C" {
      #endif
      /*
       * Class:     StaticMethodCall
       * Method:    nativeMethod
       * Signature: ()V
       */
      JNIEXPORT void JNICALL Java_StaticMethodCall_nativeMethod
        (JNIEnv *, jobject);
      
      #ifdef __cplusplus
      }
      #endif
      #endif
      
    5. C implementation of StaticMethodCall.c
    6. #include <stdio.h>
      #include <jni.h>
                                                                                      
          JNIEXPORT void JNICALL
      Java_StaticMethodCall_nativeMethod(JNIEnv *env, jobject obj)
      {
          jclass cls = (*env)->GetObjectClass(env, obj);
          jmethodID mid =
              (*env)->GetStaticMethodID(env, cls, "callback", "()V");
          if (mid == NULL) {
              return;  /* method not found */
          }
          printf("Output: In C code\n");
          (*env)->CallStaticVoidMethod(env, cls, mid);
      }
      
    7. Generate the shared library file - libStaticMethodCall.so
    8. gcc -shared -I/usr/local/jdk1.6.20_21/include -I/usr/local/jdk1.6.20_21/include/linux StaticMethodCall.c -o libStaticMethodCall.so
    9. RUN StaticmethodCall output:
    10. Output: In C code
      Output: In Java code

  • JNI Method Descriptors
    • similar to how descriptor denotes field types
    • Format:('[' + ARGUMENT_TYPE) + RETURN_TYPE
    • For example:
      native private String getLine(String);
      "(Ljava/lang/String;)Ljava/lang/String;"
      public static void main(String[] args);
      "([Ljava/lang/String;)V"
    • Use the javap tool to print out JNI method descriptors.
    • For example: javap -s -p InstanceMethodCall
    • -s: to output JNI descriptor strings rather than types as they appear in the Java programming language
    • -p: to include information about the private members of the class
    • The output:
      Compiled from "InstanceMethodCall.java"
      class InstanceMethodCall extends java.lang.Object{
      InstanceMethodCall();
        Signature: ()V
      private native void nativeMethod();
        Signature: ()V
      private void callback();
        Signature: ()V
      public static void main(java.lang.String[]);
        Signature: ([Ljava/lang/String;)V
      static {};
        Signature: ()V }

0 意見: