2010年10月4日 星期一

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

Fields Accessing
  • 這個方法的觀念很簡單,就是:既然java native function是屬於某一個class,理論上它就可以存取這個class裡所有的member fields。因此,這個class中所有的函式,無論在java還是C++,都可以對同一組資料做存取而無需透過參數傳遞。
    The Java programming language supports two kinds of fields.
    1. (Object) Each instance of a class has its own copy of the instance fields of the class.
    2. (Class) All instances of a class share the static fields of the class.
    3. The JNI provides functions that native code can use to get and set instance fields in objects and static fields in classes.
  • Access Object Fileds
  • (Example) InstanceFieldAccess.java: The main method creates an object, sets the instance field, and then calls the native method (InstanceFieldAccess.accessField) to print out the existing value of the instance field and then set the field to a new value.
    1. InstanceFieldAccess.java
    2. class InstanceFieldAccess {
          private String s;
          private native void accessField();
      
          public static void main(String args[]) {
              InstanceFieldAccess c = new InstanceFieldAccess();
              c.s = "abc";
              c.accessField();
              System.out.println("In Java:");
              System.out.println("  c.s = \"" + c.s + "\"");
          }
          static {
              System.loadLibrary("InstanceFieldAccess");
          }
      }
      
    3. Generate C header file: InstanceFieldAccess.h
    4. /* DO NOT EDIT THIS FILE - it is machine generated */                                                        
      #include <jni.h>
      /* Header for class InstanceFieldAccess */
      
      #ifndef _Included_InstanceFieldAccess
      #define _Included_InstanceFieldAccess
      #ifdef __cplusplus
      extern "C" {
      #endif
      /*
       * Class:     InstanceFieldAccess
       * Method:    accessField
       * Signature: ()V
       */
      JNIEXPORT void JNICALL Java_InstanceFieldAccess_accessField
        (JNIEnv *, jobject);
      
      #ifdef __cplusplus
      }
      #endif
      #endif
      
    5. C implementation of InstanceFieldAccess.c
    6. #include <stdio.h>
      #include <jni.h>
      
          JNIEXPORT void JNICALL 
      Java_InstanceFieldAccess_accessField(JNIEnv *env, jobject obj)
      {
          jfieldID fid;   /* store the field ID */
          jstring jstr;
          const char *str;
      
          /* Get a reference to obj's class */
          jclass cls = (*env)->GetObjectClass(env, obj);
      
          printf("In C:\n");
      
          /* Look for the instance field s in cls */
          fid = (*env)->GetFieldID(env, cls, "s", "Ljava/lang/String;");
          if (fid == NULL) {
              return; /* failed to find the field */
          }
      
          /* Read the instance field s */
          jstr = (*env)->GetObjectField(env, obj, fid);
          str = (*env)->GetStringUTFChars(env, jstr, NULL);
          if (str == NULL) {
              return; /* out of memory */
          }
          printf("  c.s = \"%s\"\n", str);
          (*env)->ReleaseStringUTFChars(env, jstr, str);
      
          /* Create a new string and overwrite the instance field */
          jstr = (*env)->NewStringUTF(env, "123");
          if (jstr == NULL) {
              return; /* out of memory */
          }
          (*env)->SetObjectField(env, obj, fid, jstr);
      }
      
        To access an instance field, the native method follows a two-step process.
      1. First, it calls GetFieldID to obtain the field ID from the class reference, field name, and field descriptor:
      2. fid = (*env)->GetFieldID(env, cls, "s", "Ljava/lang/String;");
        
      3. Once you have obtained the field ID, you can pass the object reference and the field ID to the appropriate instance field access function (ex. GetObjectField):
      4. jstr = (*env)->GetObjectField(env, obj, fid);
        
        P.S. Strings & Arrays (special objects) : use GetObjectField to access the instance fields.
               Primitive types : use GetIntField and SetFloatField for accessing instance fields.
    7. Generate shared library file - libInstanceFieldAccess.so
    8. gcc -shared -I/usr/local/jdk1.6.20_21/include -I/usr/local/jdk1.6.20_21/include/linux InstanceFieldAccess.c -o libInstanceFieldAccess.so
    9. RUN InstanceFieldAccess output:
    10. In C: c.s = "abc"
      In Java: c.s = "123"
  • Access Static Fields
    1. Accessing static fields is similar to accessing instance fields.
      (Example) StaticFielcdAccess.java: The main method creates an object, initializes the static field, and then calls the native method (StaticFieldAccess.accessField) to print out the existing value of the static field and then set the field to a new value.
    2. StaticFielcdAccess.java
    3. class StaticFieldAccess {
          private static int si;
          private native void accessField();
      
          public static void main(String args[]) {
              StaticFieldAccess c = new StaticFieldAccess();
              StaticFieldAccess.si = 100;
              c.accessField();
              System.out.println("In Java:");
              System.out.println("  StaticFieldAccess.si = " + si);
          }
          static {
              System.loadLibrary("StaticFieldAccess");
          }
      }
      
    4. Generate c header file: StaticFielcdAccess.h
    5. /* DO NOT EDIT THIS FILE - it is machine generated */                           
      #include <jni.h>
      /* Header for class StaticFieldAccess */
      
      #ifndef _Included_StaticFieldAccess
      #define _Included_StaticFieldAccess
      #ifdef __cplusplus
      extern "C" {
      #endif
      /*
       * Class:     StaticFieldAccess
       * Method:    accessField
       * Signature: ()V
       */
      JNIEXPORT void JNICALL Java_StaticFieldAccess_accessField
        (JNIEnv *, jobject);
      
      #ifdef __cplusplus
      }
      #endif
      #endif
      
    6. C implementation of StaticFieldAccess.c
    7. #include <stdio.h>
      #include <jni.h>
      
      JNIEXPORT void JNICALL 
      Java_StaticFieldAccess_accessField(JNIEnv *env, jobject obj)
      {
          jfieldID fid;   /* store the field ID */
          jint si;
      
          /* Get a reference to obj's class */
          jclass cls = (*env)->GetObjectClass(env, obj);
      
          printf("In C:\n");
      
          /* Look for the static field si in cls */
          fid = (*env)->GetStaticFieldID(env, cls, "si", "I");
          if (fid == NULL) {
              return; /* field not found */
          }
          /* Access the static field si */
          si = (*env)->GetStaticIntField(env, cls, fid);
          printf("  StaticFieldAccess.si = %d\n", si);
          (*env)->SetStaticIntField(env, cls, fid, 200);
      }
      
    8. Generate shared library file - libStaticFieldAccess.so
    9. gcc -shared -I/usr/local/jdk1.6.20_21/include -I/usr/local/jdk1.6.20_21/include/linux StaticFieldAccess.c -o libStaticFieldAccess.so
    10. RUN StaticFieldAccess output:
    11. In C: StaticFieldAccess.si = 100
      In Java: StaticFieldAccess.si = 200
    There are two differences between how access a static field and how access an instance field:
    • Usage: call GetStaticFieldID for static fields, as opposed to GetFieldID for instance fields. GetStaticFieldID and GetFieldID have the same return type jfieldID.
    • Once you have obtained the static field ID, you pass the class reference, as opposed to an object reference, to the appropriate static field access function (such as GetStaticIntField for static int field).
  • JNI Field Descriptors
    • The specially encoded C string "Ljava/lang/String;" to represent a field type (such as java.lang.String) in the Java programming language. These C strings are called JNI field descriptors.
    • Descriptors Query Tool: "javap" tool (shipped with JDK or Java 2 SDK releases) to generate the field descriptors from class files.
    • javap -s -p CLASS_NAME
      • -s option: print internal type signatures
      • -p option: exposing private members


1 意見:

J.A Yan 提到...

想不到在找JNI的資訊時會遇到老朋友,哈哈