2010年9月27日 星期一

JNI - Arguments Passing: Basic Types, Strings and Primitive Arrays

在此講述使用JNI在Java與C/C++中傳遞參數的方式,並以簡單的例子做示範。

  • Accessing Strings
    1. (Example) Prompt.javacontains a native method that prints a string, waits for user input, and then returns the line that the user has typed in.
    2. Prompt.java
      class Prompt {
           // native method that prints a prompt and reads a line
           private native String getLine(String prompt);
       
           public static void main(String args[]) {
               Prompt p = new Prompt();
               String input = p.getLine("Type a line: ");
               System.out.println("User typed: " + input);
           }
           static {
               System.loadLibrary("Prompt");
           }
       }
      
      
    3. Generate C/C++ header file by "javah -jni CLASS_NAME (Prompt)"
    4. Prompt.h
      /* DO NOT EDIT THIS FILE - it is machine generated */
      #include <jni.h>
      /* Header for class Prompt */
      
      #ifndef _Included_Prompt
      #define _Included_Prompt
      #ifdef __cplusplus
      extern "C" {
      #endif
      /*
       * Class:     Prompt
       * Method:    getLine
       * Signature: (Ljava/lang/String;)Ljava/lang/String;
       */
      JNIEXPORT jstring JNICALL Java_Prompt_getLine
        (JNIEnv *, jobject, jstring);
      
      #ifdef __cplusplus
      }
      #endif
      #endif
      
      The name of the C function is formed by concatenating the "Java_" prefix, the "CLASS_NAME", and the "METHOD_NAME". Parameters of Java_Prompt_getLine():
      • JNIEnv *: The first parameter, the JNIEnv interface pointer, points to a location that contains a pointer to a function table. A JNI interface pointer (JNIEnv*) is passed as an argument for each native function mapped to a Java method, allowing for interaction with the JNI environment within the native method.(from Wiki)
      • jobject: The second argument differs depending on whether the native method is a static or an instance method.
        • The second argument to an instance native method is a reference to the object on which the method is invoked, similar to the "this" pointer in C++.
        • The second argument to a static native method is a reference to the class in which the method is defined.
    5. Implementation of Prompt.c
      • Native method code must use the appropriate JNI functions to convert jstring objects to C/C++ strings.
      (version 1 for JDK1.1)Prompt.c
      #include <stdio.h>
      #include <jni.h>
      #include "Prompt.h"
      
      JNIEXPORT jstring JNICALL 
       Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
       {
           char buf[128];
           const jbyte *str;
           str = (*env)->GetStringUTFChars(env, prompt, NULL);
           if (str == NULL) {
               return NULL; /* OutOfMemoryError already thrown */
           }
           printf("%s", str);
           (*env)->ReleaseStringUTFChars(env, prompt, str);
           /* We assume here that the user does not type more than
            * 127 characters */
           scanf("%s", buf);
           return (*env)->NewStringUTF(env, buf);
       }
      
      • GetStringUTFChars: through the JNIEnv interface pointer to read the contents of the string, and converts the jstring reference from Unicode(JAVA) to UTF-8(C/C++).
      • Do not forget to check the return value of GetStringUTFChars.
        • A chance to fail memory allocation: Because the JVM implementation needs to allocate memory to hold the UTF-8 string. When it happens, GetStringUTFChars returns NULL and throws an OutOfMemoryError exception.
      • ReleaseStringUTFChars: When no longer needs the UTF-8 string returned by GetStringUTFChars; Free the memory taken by the UTF-8 string.
      • New JNI String Functions in Java 2 SDK Release 1.2
        (version 2 for JDK1.2)Prompt2.c
        #include <stdio.h>
        #include <jni.h>
        #include "Prompt.h"
        
        JNIEXPORT jstring JNICALL 
          Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
          {
            /* assume the prompt string and user input has less than 128
               characters */
            char outbuf[128], inbuf[128];
            int len = (*env)->GetStringLength(env, prompt);
            (*env)->GetStringUTFRegion(env, prompt, 0, len, outbuf);                    
            printf("%s", outbuf);
            scanf("%s", inbuf);
            return (*env)->NewStringUTF(env, inbuf);
          }
        
      • GetStringUTFRegion: Copies the content of a string to or from a pre-allocated C buffer in the UTF-8 format. It performs no memory allocation, we need not check for possible out-of-memory conditions.
      • (The above code, however, lacks the necessary checks to ensure that the prompt string contains less than 128 characters.)
    Summary of JNI String Functions

    Choosing among JNI String Functions


  • Accessing Arrays
  • The JNI treats primitive arrays and object arrays differently.
    • Primitive arrays: Primitive arrays contain elements that are of primitive types such as int and boolean. ex. int[ ] iarr; float[ ] farr;
    • Object arrays: Object arrays contain elements that are of reference types such as class instances and other arrays. ex. Object[ ] oarr; int[ ][ ] arr2;
    1. (Example) IntArray.java: calls a native method sumArray that adds up the contents of an int array.
    2. IntArray.java
      class IntArray {                                                                
          private native int sumArray(int[] arr);
          public static void main(String[] args) {
              IntArray p = new IntArray();
              int arr[] = new int[10];
              for (int i = 0; i < 10; i++) {
                  arr[i] = i;
              }
              int sum = p.sumArray(arr);
              System.out.println("sum = " + sum);
          }
          static {
              System.loadLibrary("IntArray");
          }
      }
      
      
      
    3. Generate C/C++ header file by "javah -jni CLASS_NAME (IntArray)"
    4. IntArray.h
      /* DO NOT EDIT THIS FILE - it is machine generated */                           
      #include <jni.h>
      /* Header for class IntArray */
      
      #ifndef _Included_IntArray
      #define _Included_IntArray
      #ifdef __cplusplus
      extern "C" {
      #endif
      /*
       * Class:     IntArray
       * Method:    sumArray
       * Signature: ([I)I
       */
      JNIEXPORT jint JNICALL Java_IntArray_sumArray
        (JNIEnv *, jobject, jintArray);
      
      #ifdef __cplusplus
      }
      #endif
      #endif
      
      
      
    5. Implementation of IntArray.c
    6. version 1 using GetIntArrayRegion() - IntArray.c
      #include <jni.h>
      #include "IntArray.h"
      
      JNIEXPORT jint JNICALL 
        Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)
        {
           jint buf[10];
           jint i, sum = 0;
           (*env)->GetIntArrayRegion(env, arr, 0, 10, buf);
           for (i = 0; i < 10; i++) {
               sum += buf[i];
           }
           return sum;
        }
      
      • GetIntArrayRegion: to copy all the elements in the integer array into a C buffer (pre-allocated buf).
        • The third argument is the starting index of the elements.
        • The fourth argument is the number of elements to be copied.
      version 2 using GetIntArrayElements() - IntArray2.c
      #include <jni.h>
      #include "IntArray.h"
      
      JNIEXPORT jint JNICALL 
        Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)
        {
          jint *carr;
          jint i, sum = 0;
          carr = (*env)->GetIntArrayElements(env, arr, NULL);
          if (carr == NULL) {
              return 0; /* exception occurred */
          }
          for (i=0; i<10; i++) {
              sum += carr[i];
          }
          (*env)->ReleaseIntArrayElements(env, arr, carr, 0);
          return sum;
        }
      
      • The JNI supports a family of Get/Release<type>ArrayElements functions that allow the native code to obtain a direct pointer to the elements of primitive arrays. Because the underlying garbage collector may not support pinning, the virtual machine may return a pointer to a copy of the original primitive array.
    Summary of JNI Primitive Array Functions

    Choosing among JNI Primitive Array Functions

0 意見: