2010年12月3日 星期五

Qt Memory Management & Signal and Slots

Check out this SlideShare Presentation:

2010年11月1日 星期一

Qt: QRegion Class 圖形交疊的四種取出作法

Creating and Using Regions

當在Qt使用QPainter繪圖時,常常會遇到所需繪出圖形並非單純的長方形(Rectangle)或多邊形(polygon),可能是多種圖形的交錯合體情況,以下說明當圖形重疊時,可取得相交區域的四種情形。

General Usage:
void MyWidget::paintEvent(QPaintEvent *)
{
    QRegion r1(QRect(100, 100, 200, 80), QRegion::Ellipse);
    // r1: elliptic region
    QRegion r2(QRect(100, 120, 90, 30));    // r2: rectangular region
    QRegion r3 = r1.intersected(r2);        // r3: intersection
    QPainter painter(this);
    painter.setClipRegion(r3);
    ...                                     // paint clipped graphics
    painter.drawrect(QRect(100, 100, 200, 80));
}

  1. 交集-僅取出兩者相疊區域(intersected):
    • QRegion QRegion::intersected ( const QRegion & r ) const
    • 圖例:
  2. 僅取出前者未與後者重疊區域(subtracted):
    • QRegion QRegion::subtracted ( const QRegion & r ) const
    • 圖例:
  3. 聯集-合併取出兩者所有區域(united):
    • QRegion QRegion::united ( const QRegion & r ) const
    • 圖例:
  4. 取出兩者不重疊的區域(xored):
    • QRegion QRegion::xored ( const QRegion & r ) const
    • 圖例:



更詳細資料請參考:Qt4.6 QRegion Class Reference

2010年10月22日 星期五

讓Putty能夠顯示中文字 in Ubuntu

在Ubuntu下,要讓Putty顯示中文字主要設定方式可分為兩個步驟

  • 設定Putty Translation編碼,如下圖所示
    1. 選擇 Translation >> Remote character set: UTF-8

  • 設定Putty window Fonts,如下圖所示
    1. 選擇 Fonts >> 按下 Font used for ordinary text (Change...)
    2. 選擇字型 "TlwgMono"

大功告成!


--- 特別感謝 David 技術教學 ---

2010年10月19日 星期二

Colorful man pages setting

  • Linux colorful man pages setting (for bash)
    • 開啟個人bash設定檔
    vi ~/.bashrc
  • 加入以下資料
  • # For colourful man pages
    export PAGER="`which less` -s"
    export BROWSER="$PAGER"
    export LESS_TERMCAP_mb=$'\E[01;34m'
    export LESS_TERMCAP_md=$'\E[01;34m'
    export LESS_TERMCAP_me=$'\E[0m'
    export LESS_TERMCAP_se=$'\E[0m'
    export LESS_TERMCAP_so=$'\E[01;44;33m'
    export LESS_TERMCAP_ue=$'\E[0m'
    export LESS_TERMCAP_us=$'\E[01;33m'
    
  • 讀取bash設定檔
  • source ~/.bashrc
  • 完成!

2010年10月14日 星期四

JNI - Local and Global References

  • Local References
  • A local reference is valid only within the dynamic context of the native method that creates it, and only within that one invocation of the native method.
    P.S. You must not write native methods that store a local reference in a static variable and expect to use the same reference in subsequent invocations.
    (Example) MyNewString(): It is a modified version of the MyNewString function in before, uses local references incorrectly.
    /* This code is illegal */
    jstring
    MyNewString(JNIEnv *env, jchar *chars, jint len)
    {
        static jclass stringClass = NULL;
        jmethodID cid;
        jcharArray elemArr;
        jstring result;
    
        if (stringClass == NULL) {
            stringClass = (*env)->FindClass(env, "java/lang/String");
            if (stringClass == NULL) {
                return NULL; /* exception thrown */
            }
        }
        /* It is wrong to use the cached stringClass here, because it may be invalid. */
        cid = (*env)->GetMethodID(env, stringClass, "<init>", "([C)V");
        ...
        elemArr = (*env)->NewCharArray(env, len);
        ...
        result = (*env)->NewObject(env, stringClass, cid, elemArr);
        (*env)->DeleteLocalRef(env, elemArr);
        return result;
    }
    
    The goal for caching stringClass in a static variable might have been to eliminate the overhead of repeatedly making the following function call:
    FindClass(env, "java/lang/String");
    
    This is not the right approach because FindClass returns a local reference to the java.lang.String class object. To see why this is a problem, suppose that the native method implementation of C.f calls MyNewString:
    JNIEXPORT jstring JNICALL
    Java_C_f(JNIEnv *env, jobject this)
    {
        char *c_str = ...;
        ...
        return MyNewString(c_str);
    }
    
  • Global References

2010年10月11日 星期一

Caching Field and Method IDs

Obtaining field and method IDs requires symbolic lookups based on the name and descriptor of the field or method. Symbolic lookups are relatively expensive.
How to reduce this overhead?
The idea is to compute field and method IDs and cache them for repeated uses later.
  • Caching at the Point of Use
  • Field and method IDs may be cached at the point where native code accesses the field values or performs method callbacks.
    (Example)InstanceFieldAccess.c: caches the field ID in static variables so that it need not be recomputed upon each invocation of the InstanceFieldAccess.accessField method.
    #include <stdio.h>
    #include <jni.h>
    
    JNIEXPORT void JNICALL 
    Java_InstanceFieldAccess_accessField(JNIEnv *env, jobject obj)
    {
        static jfieldID fid_s = NULL; /* cached field ID for s */
        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 */
        if (fid_s == NULL) {
            fid_s = (*env)->GetFieldID(env, cls, "s", "Ljava/lang/String;");
            if (fid_s == NULL) {
                return; /* exception already thrown */
            }
        }
    
        /* Read the instance field s */
        jstr = (*env)->GetObjectField(env, obj, fid_s);
        str = (*env)->GetStringUTFChars(env, jstr, NULL);
        if (str == NULL) {
            return; /* out of memory */
        }
        printf("  c.s = \"%s\"\n", str);
        (*env)->ReleaseStringUTFChars(env, jstr, str);
    
        jstr = (*env)->NewStringUTF(env, "123");
        if (jstr == NULL) {
            return; /* out of memory */
        }
        (*env)->SetObjectField(env, obj, fid_s, jstr);
    }
    
    • The static variable fid_s stores the precomputed field ID for "InstanceFieldAccess.s".
    • When the InstanceFieldAccess.accessField method is called for the first time, it computes the field ID and caches it in the static variable for later use.
  • Caching in the Defining Class's Initializer
  • If we use "caching at the Point of Use", it runs into some situations (weakness):
    • It incurs a small performance impact on the "fast path" when the IDs have already been cached.
    • It could lead to duplication of caching and checking.
    It is more convenient to initialize the field and method IDs required by a native method before the application can have a chance to invoke the native method.(Improvement)
    • a suitable place for computing and caching field or method IDs is in the static initializer of the class that defines the fields or methods.
    1. (Example) InstanceMethodCall.java: To cache the method ID for InstanceMethodCall.callback(), we introduce a new native method initIDs that called from the static initializer of the InstanceMethodCall class.
    2. class InstanceMethodCall {
          private static native void initIDs();
          private native void nativeMethod();
          private void callback() {
              System.out.println("In Java");
          }
          public static void main(String args[]) {
              InstanceMethodCall c = new InstanceMethodCall();
              c.nativeMethod();
          }
          static {
              System.loadLibrary("InstanceMethodCall");
              initIDs();
          }
      }
      
    3. Regenerate the C native 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:    initIDs
       * Signature: ()V
       */
      JNIEXPORT void JNICALL Java_InstanceMethodCall_initIDs
        (JNIEnv *, jclass);
      
      /*
       * Class:     InstanceMethodCall
       * Method:    nativeMethod
       * Signature: ()V
       */
      JNIEXPORT void JNICALL Java_InstanceMethodCall_nativeMethod
        (JNIEnv *, jobject);
      
      #ifdef __cplusplus
      }
      #endif
      #endif
      
    5. C implementation file (InstanceMethodCall.c):
    6. #include <stdio.h>
      #include <jni.h>
      
      jmethodID MID_InstanceMethodCall_callback;
      
          JNIEXPORT void JNICALL
      Java_InstanceMethodCall_initIDs(JNIEnv *env, jclass cls)
      {
          MID_InstanceMethodCall_callback = (*env)->GetMethodID(env, cls, "callback", "()V");
      }
      
          JNIEXPORT void JNICALL
      Java_InstanceMethodCall_nativeMethod(JNIEnv *env, jobject obj)
      {
          printf("Output: In C code\n");
          (*env)->CallVoidMethod(env, obj, MID_InstanceMethodCall_callback);
      }
      
      • The implementation of initIDs() simply computes and caches the method ID for InstanceMethodCall.callback().
      • The JVM runs the static initializer, and in turn calls the initIDs() method, before executing any other methods in the InstanceMethodCall class. With the method ID is already cached in a global variable, the native implementation of InstanceMethodCall.nativeMethod() no longer needs to perform a symbolic lookup.
      • RUN the InstanceMethodCall:
      • Output: In C code
        Output: In Java code
  • Comparison between the Two Approaches to Caching IDs
    • Caching IDs at the point of use:
      • (Advantage) - The reasonable solution if the JNI programmer does not have control over the source of the class that defines the field or method.
      • (Disadvantage) -
        1. Requires a check in the execution fast path and may also require duplicated checks and initialization of the same field or method ID.
        2. Method and field IDs are only valid until the class is unloaded.
    • Caching in the Defining Class's Initializer:
      • (Advantage) - Caching is done in the static initializer of the defining class, the cached IDs will automatically be recalculated when the class is unloaded and later reloaded.
  • Performance of JNI Field and Method Operations
    • How does the cost of ...
      1. performing a callback from native code (a Native/Java callback)?
      2. calling a native method (a Java/Native call)?
      3. calling a regular method (a Java/Java call)?
      Java/native calls are potentially slower than Java/Java calls for the following reasons:
      • Native methods most likely follow a different calling convention than that used by Java/Java calls inside the Java virtual machine implementation. As a result, the virtual machine must perform additional operations to build arguments and set up the stack frame before jumping to a native method entry point.
      • It is common for the virtual machine to inline method calls. Inlining Java/native calls is a lot harder than inlining Java/Java calls.
      The typical virtual machine may execute a Java/native call roughly two to three times slower than it executes a Java/Java call.
      In theory, the overhead of native/Java callbacks could also be within two to three times of Java/Java calls.

2010年10月6日 星期三

JNI - Invoking Constructors

In the JNI, constructors may be invoked following steps similar to those used for calling instance methods.
    Invoke constructors as following steps:
  1. To obtain the method ID of a constructor, pass "<init>" as the method name and "V" as the return type in the method descriptor.
  2. To invoke the constructor by passing the method ID to JNI functions such as NewObject.
  3. (Example) The following code implements the equivalent functionality of the JNI function NewString, which constructs a java.lang.String object from the Unicode characters stored in a C buffer:
jstring MyNewString(JNIEnv *env, jchar *chars, jint len)
{
    jclass stringClass;
    jmethodID cid;
    jcharArray elemArr;
    jstring result;

    stringClass = (*env)->FindClass(env, "java/lang/String");
    if (stringClass == NULL) {
        return NULL; /* exception thrown */
    }
    /* Get the method ID for the String(char[]) constructor */
    cid = (*env)->GetMethodID(env, stringClass, "<init>", "([C)V");
    if (cid == NULL) {
        return NULL; /* exception thrown */
    }

    /* Create a char[] that holds the string characters */
    elemArr = (*env)->NewCharArray(env, len);
    if (elemArr == NULL) {
        return NULL; /* exception thrown */
    }
    (*env)->SetCharArrayRegion(env, elemArr, 0, len, chars);

    /* Construct a java.lang.String object */
    result = (*env)->NewObject(env, stringClass, cid, elemArr);
 
    /* Free local references */
    (*env)->DeleteLocalRef(env, elemArr);
    (*env)->DeleteLocalRef(env, stringClass);
    return result;
}
  1. FindClass(): returns a reference to the java.lang.String class.
  2. GetMethodID(): returns the method ID for the string constructor, String(char[] chars).
  3. NewCharArray(): to allocate a character array that holds all the string elements.
  4. NewObject(): invokes the constructor specified by the method ID.
  5. DeleteLocalRef(): allows the virtual machine to free the resources used by local references elemArr and stringClass.
It is also possible to invoke constructors using the CallNonvirtualVoidMethod function.
    result = (*env)->NewObject(env, stringClass, cid, elemArr);
  • The single NewObject call above may be replaced by an AllocObject call followed by a CallNonvirtualVoidMethod call:
  • result = (*env)->AllocObject(env, stringClass);
    if (result) {
        (*env)->CallNonvirtualVoidMethod(env, result, stringClass, cid, elemArr);
        /* we need to check for possible exceptions */
        if ((*env)->ExceptionCheck(env)) {
            (*env)->DeleteLocalRef(env, result);
            result = NULL;
        }
    }
    
    AllocObject(): creates an uninitialized object.
    CallNonvirtualVoidMethod(): invokes constructors.
    In most cases, however, you should use NewObject and avoid the more error-prone AllocObject/CallNonvirtualVoidMethod pair.

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 }

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


2010年9月29日 星期三

(轉錄) [Java] Static 的意義與實作方式

Static 的意義與實作方式

「將某 class 產生出一個 instance 之後,此 class 所有的 instance field 都會新增一份,那麼所有的 instance method 是否也會新增一份?」我常常看到網路上有人在討論此問題,所以寫了這篇文章,以為解釋。

Member 的種類
類別(class)的內部組成統稱為成員(member),如果依據成員是「資料」還是「程式」來區分,可以分成:
資料,被稱為 field
程式,被稱為 method
如果再依據有無使用 static 修飾而細分,則成員可細分成四種:

  • class field:有用 static 修飾的 field
  • class method:有用 static 修飾的 method
  • instance field:沒有用 static 修飾的 field
  • instance method:沒有用 static 修飾的 method

顧名思義,class field/method 和 class 本身有密切的關係,而 instance field/method 則與 instance(也就是物件)有密切的關係。請看下面的範例程式。

public class Class1 {
  public static int classField;
  public static void classMethod1() {
    // ...
  }
  public static void classMethod2() {
    // ...
  }

  public int instanceField;
  public void instanceMethod1() {
    // ...
  }
  public void instanceMethod2() {
    // ...
  }
}

public class Class2 {
  public static void classMethod () {
    // ...
  }
  public void instanceMethod() {
    // ...
  }
}

Field
宣告 field 時,如果前面加上 static 的修飾字,就會使得此 field 變成是 class field。一個 class field 永遠只佔用一塊記憶體,而且此記憶體空間是在此 class 一被載入(load)記憶體之後就立刻配置的(allocate),感覺就如同此 field 與該 class 本身相關,而不是與該 class 的 instance 相關。class field 可以被同一個 class 的 class method 內部所直接使用,舉例來說,上述的 classMethod1() 內部可以出現 classField。如果 Class1 的 class method 或 instance method 欲使用到 Class2 的 class field,就必須在前面冠上 Class2,例如:Class2.classField。


宣告field時,如果前面「不」加上 static 的修飾字,就會使得此 field 變成是 instance field。對 instance field 而言,每產生一個instance(物件)就多一塊 instance field 的記憶體,每少一個 instance 就少一塊 instance field 的記憶體。instance field 可以被同一個 instance的 instance method 內部所直接使用,舉例來說,上述的 instanceMethod1() 內部可以出現 instanceField。如果某 class 的class method 或 instance method 欲使用到某 instance 的 instance field,就必須在前面冠上 instance 名稱,例如:obj.classField。



Method
宣告 method 時,如果前面加上 static 的修飾字,就會使得此 method 變成是 class method。對 class method 而言,永遠只佔用一塊記憶體,而且此記憶體空間是在此 class 一被載入進記憶體之後就立刻配置的,就如同此 method 是與該 class 本身相關,而不是與該 class 的 instance 相關。class method 可以被同一個 class 的任何 class method 內部所直接使用,舉例來說,上述的classMethod1() 內部可以出現 classMethod2()。如果 Class1 的 class method 或 instance method 欲使用到 Class2 的 classMethod(),就必須在前面冠上 Class2,也就是 Class2.classMethod()。


宣告 method 時,如果前面「不」加上 static 的修飾字,就會使得此 method 變成是 instance method。對 instance method 而言,每產生一個 instance「並不會」多一塊 instance method 的記憶體。同一個 method 不管被調用(invoke)幾次,也不管被調用時的instance 是何者,每次的程式碼完全都一樣,差別只在每次執行時資料不同,而資料是存放在 call stack 中,所以不會混淆。在 instance method 內,資料的來源包括了參數和 instance field。參數被傳進來變成 call stack 內的 entry,所以不會混淆,這很容易理解,但是 instance field 是如何區隔開來的(前面剛提過:instance field 會隨著 instance 數目增加而增加),這是透過隱匿(implicit)的 this 參數來達到了,稍後會有說明。instance method 可以被同一個 instance 的 instance method 內部所直接使用,舉例來說,上述的 instanceMethod1() 內部可以出現 instanceMethod2()。如果某 class 的 class method 或 instance method 欲使用到某 instance 的某 instance method,就必須在前面冠上此 instance 名稱,例如:obj.classMethod()。



隱匿的 this 參數
綜合上面的敘述來看:

  • class field:共用一塊記憶體
  • class method:共用一塊記憶體
  • instance field:隨著每個 instance 各有一塊記憶體
  • instance method:共用一塊記憶體

instance method 為什麼不是隨著每個 instance 佔有一塊記憶體,反倒是共用一塊記憶體?其實,讓每個 instance method 如同instance field 一樣,隨著每個 instance 佔有一塊記憶體,這麼做當然是可以的,只是 Java 編譯器和 JVM 都不這麼做,因為太浪費記憶體空間了。一個 field 少則佔用一個 byte,多則佔用數百 Byte,但是 method 少則數個 byte,多則數百 Kilo Byte。Mehtod耗費的記憶體是 field 的數百倍,甚至數千倍,當然是能共用就盡量共用,比較不會消耗記憶體。既然 JVM 讓一個 class 的所有instance 共用相同的 instance method,下面兩行程式碼在 instanceMethod() 內部時,如何區分是 instance1 或 instance2?
instance1.instanceMethod();
instance2.instanceMethod();

因為編譯器會幫我們在把 instance1 和 instance2 個別傳入 instanceMethod() 中當作第一個參數。也就是說,任何 instance method 參數的實際個數都會比表面上多一個,這個多出來的參數是由 Java 編譯器幫我們加上去的,用來代表對應的 instance。此參數的變數名稱為 this,也是 Java 的一個關鍵字(keyword)。

當調用某個 instance method 或使用某個 instance field 時,你必須在前面加上該 instance 的名稱,如果該 instance method/field 相關的 instance 和當時程式碼所在的 instance method 的 instance 指的是同一個 instance 時,該 instance 的名稱就是 this,這種情況下,你也可以選擇不在前面加上「this.」。

然而,在某些狀況下,非得在前面加上「this.」不可。例如,當method中的參數或區域變數和 instance field 名稱完全相同時,如果不在前面冠上「this.」,那麼指的是參數或區域變數;如果在前面冠上「this.」,那麼指的才是 instance field。

本文作者:蔡學鏞
張貼日期:09/09/2002
參考自[Java] Static 的意義與實作方式

2010年9月28日 星期二

Java Native Interface (JNI) - Introduction

Why JNI?
  • Native applications are written in native programming languages such as C and C++, compiled into host-specific binary code, and linked with native libraries.
    • Native applications and native libraries are typically dependent on a particular host environment.
    • A C application built for one operating system, for example, typically does not work on other operating systems.
  • Java platforms are commonly deployed on top of a host environment.
    • The Java platform offers a set of features that applications can rely on independent of the underlying host environment.
    JNI allows Java code running in a Java Virtual Machine (JVM) to call and to be called by native applications and libraries written in other languages, such as C, C++.
    主要基於以下三個需求因應而生:
  1. 解決性能問題
    • 採用JNI技術針對一些嚴重影響Java性能的部分程式碼,該部分可能只占原始程式的極少部分,所以幾乎可以不考慮該部分代碼在主流平臺之間移植的工作量。同時,也不必過分擔心類型匹配問題,我們完全可以控制程式碼不出現這種錯誤。此外,也不必擔心安全控制問題,因爲Java安全模型已擴展爲允許非系統類別載入和呼叫本地方法。根據Java規範,從JDK 1. 2開始,FindClass將設法找到與當前的本地方法關聯的類別載入器。如果平臺相關程式碼屬於一個系統類別,則無需涉及任何類別載入器;否則,將呼叫適當的類別載入器來載入和鏈結已命名的類別。換句話說,如果在Java程式中直接呼叫C/C++語言産生的機器碼,該部分代碼的安全性就由Java虛擬機器控制。
  2. 解決本機平臺介面呼叫問題
    • 由於Java跨平臺的特性,使得它與本機的各種內部聯繫變得很少,約束了它的功能。通過JNI呼叫native methods,使Java可以實現和本地機器的緊密聯繫,呼叫系統級的各介面方法。而native methods是以shared library文件的形式存放的(在WINDOWS平臺上是DLL文件形式,在UNIX機器上是SO文件形式)
  3. 嵌入式開發應用
    • Write once, run anywhere (WORA)
    • Java應用程式能夠在帶有JVM的任何硬軟體系統上執行。加上Java語言本身所具有的安全性、可靠性和可攜性等特點,對實現瘦身上網的資訊家電等網路設備十分有利。也正是由於JNI解決了本機平臺介面呼叫問題,於是JNI在嵌入式開發領域也是炙手可熱。

Role of JNI!
    A two-way interface that allows Java applications to invoke native code and vice versa.
  • Users use the JNI to write native methods that allow Java applications to call functions implemented in native libraries. Java applications call native methods in the same way that they call methods implemented in the Java programming language.
  • The JNI supports an invocation interface that allows users to embed a Java virtual machine implementation into native applications. Native applications can link with a native library that implements the Java virtual machine, and then use the invocation interface to execute software components written in the Java programming language.


When to use JNI?
  1. To access system features not otherwise available in the Java language or an API.
  2. To hook to existing libraries of usable native code.
  3. To improve the performance of time-critical methods.

Implications of Using the JNI
  1. Java applications that depend on the JNI can no longer readily run on multiple host environments.
    • Necessary to recompile the part of the application written in native programming languages.
    • Write once, run anywhere” would be lost.
  2. Java programming language is type-safe and secure, native languages such as C or C++ are not.
    • A misbehaving native method can corrupt the entire application. Java applications are subject to security checks before invoking JNI features.

Referenced from "The Java Native Interface - Programmer's Guide and Specification" Chapter 1

2010年9月27日 星期一

JNI - Arguments Passing: Object Arrays

  • Accessing Object Arrays
    1. (example) ObjectArrayTest.java: calls a native method to create a two-dimensional array of int and then prints the content of the array.
    2. class ObjectArrayTest {
          private static native int[][] initInt2DArray(int size);
          public static void main(String[] args) {
              int len = 5;
              int[][] i2arr = initInt2DArray(len);
              for (int i = 0; i < len; i++) {
                  for (int j = 0; j < len; j++) {
                      System.out.print(" " + i2arr[i][j]);
                  }
                  System.out.println();
              }
          }
          static {
              System.loadLibrary("ObjectArrayTest");
          }
      }
      
    3. Generate C/C++ header file
    4. ObjectArrayTest.h
      /* DO NOT EDIT THIS FILE - it is machine generated */                           
      #include <jni.h>
      /* Header for class ObjectArrayTest */
      
      #ifndef _Included_ObjectArrayTest
      #define _Included_ObjectArrayTest
      #ifdef __cplusplus
      extern "C" {
      #endif
      /*
       * Class:     ObjectArrayTest
       * Method:    initInt2DArray
       * Signature: (I)[[I
       */
      JNIEXPORT jobjectArray JNICALL Java_ObjectArrayTest_initInt2DArray
        (JNIEnv *, jclass, jint);
      
      #ifdef __cplusplus
      }
      #endif
      #endif
      
    5. Implementation of ObjectArrayTest.c
    6. ObjectArrayTest.c
      • The static native method initInt2DArray creates a two-dimensional array of the given size.
      JNIEXPORT jobjectArray JNICALL
       Java_ObjectArrayTest_initInt2DArray(JNIEnv *env, jclass cls, int size)
       {
           jobjectArray result;
           int i;
           jclass intArrCls = (*env)->FindClass(env, "[I");
           if (intArrCls == NULL) {
               return NULL; /* exception thrown */
           }
           result = (*env)->NewObjectArray(env, size, intArrCls,
                                           NULL);
           if (result == NULL) {
               return NULL; /* out of memory error thrown */
           }
           for (i = 0; i < size; i++) {
               jint tmp[256];  /* make sure it is large enough! */
               int j;
               jintArray iarr = (*env)->NewIntArray(env, size);
               if (iarr == NULL) {
                   return NULL; /* out of memory error thrown */
               }
               for (j = 0; j < size; j++) {
                   tmp[j] = i + j;
               }
               (*env)->SetIntArrayRegion(env, iarr, 0, size, tmp);
               (*env)->SetObjectArrayElement(env, result, i, iarr);
               (*env)->DeleteLocalRef(env, iarr);
           }
           return result;
       }
      
      • FindClass(): to obtain a reference of the element class of the two-dimensional int array.
      • FindClass() returns NULL and throws an exception if class loading fails. (for example, a missing class file or an out-of-memory condition)
      • The "[I" argument to FindClass is the JNI class descriptor that corresponds to the int[ ] type in the Java programming language.
      • NewObjectArray(): allocates an array whose element type is denoted by the intArrCls class reference.
      • NewIntArray(): allocates the individual array elements.
      • SetIntArrayRegion(): copies the contents of the tmp[ ] buffer into the newly allocated one-dimensional arrays.
      • SetObjectArrayElement(): sets the jth element of the ith one-dimensional array has value i+j.
      • DeleteLocalRef(): ensures that the virtual machine does not run out of the memory used to hold JNI references such as iarr.
    7. Generate shared library file - libObjectArrayTest.so
    8. cc -shared -I/usr/local/jdk1.6.20_21/include -I/usr/local/jdk1.6.20_21/include/linux ObjectArrayTest.c -o libObjectArrayTest.so
    9. Run ObjectArrayTest output (size=5):
    10. 0 1 2 3 4
      1 2 3 4 5
      2 3 4 5 6
      3 4 5 6 7
      4 5 6 7 8

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

2010年9月23日 星期四

Using Java Native Interface on Ubuntu 10.04

從無到有: to achieve Java Native Interface(JNI) on Ubuntu 10.04
  • How to install Java SDK 1.6 on Ubuntu (manual)
    1. Download the Java JDK 1.6 from Sun's site.
    2. http://java.sun.com/javase/downloads/index.jsp 
    3. Select the Java SE Development Kit (JDK)
    4. This leads to a page allowing us to select the Operating System, usually we just choose "Linux".
    5. Then we came to a page which shows the available packages. I click on "jdk-6u21-linux-i586.bin" because this version is a package resembling to a tar.gz
    6. Once the newly downloaded file is here, we need to extract it to a folder named "jdk1.6.0_21" (for example).
    7. It's better then to move  Jdk1.6.0_21 folder to "/usr/local/" which is the standard folder for programs or GUI console.
    8. We will proceed to add the JAVA_HOME variable to the system. To do this, open bash.bashrc file with the following command:
    9. $sudo vi /etc/bash.bashrc
      At the end of the file put the following instructions.
      export JAVA_HOME=/usr/local/jdk1.6.0_21
      
      export PATH=$JAVA_HOME/bin:$PATH
      This makes sure that the JAVA_HOME variable is always available in the system.
    10. Open a new console and run the command:
    11. $java -version
      To which we assume the following result:
      java version "1.6.0_21"
      Java(TM) SE Runtime Environment (build 1.6.0_21-b06)
      Java HotSpot(TM) Server VM (build 17.0-b16, mixed mode)
  • Achieve a simple JNI implementation on Ubuntu (Java call C/C++)
  • Example: To Pass a string from Java to native method (C++) and prints personalized Hello World.
    1. HelloWorlo.java: main function to call native method
    2. class HelloWorld {
          public native void sayHi(String name);
          static
          {
              System.loadLibrary("HelloWorld");
          }
          public static void main(String[] args)
          {
              HelloWorld h = new HelloWorld();
              h.sayHi("Ryan Cho");
          }
      }
      
      We announce our intention to use native method "sayHi" from "HelloWorld" library -- System.loadLibrary("HelloWorld").
    3. Compile this class and generate class file "HelloWorld.class":
    4. javac HelloWorld.java
    5. Generate the header for native method:
    6. javah -jni HelloWorld
      The header file for native method "HelloWorld.h" is generated.
      HelloWorld.h: The header file for native method (automatic generated)
      /* DO NOT EDIT THIS FILE - it is machine generated */
      #include <jni.h>
      /* Header for class HelloWorld */
      
      #ifndef _Included_HelloWorld
      #define _Included_HelloWorld
      #ifdef __cplusplus
      extern "C" {
      #endif
      /*
       * Class:     HelloWorld
       * Method:    sayHi
       * Signature: (Ljava/lang/String;)V
       */
      JNIEXPORT void JNICALL Java_HelloWorld_sayHi
        (JNIEnv *, jobject, jstring);
      
      #ifdef __cplusplus
      }
      #endif
      #endif
      
    7. Write implementation C++ file (HelloWorld.cpp):
    8. HelloWorld.cpp: The implementation file for native method.
      #include <jni.h>
      #include <iostream>
      #include "HelloWorld.h"
      
      using namespace std;
      
      JNIEXPORT void JNICALL Java_HelloWorld_sayHi
        (JNIEnv *env, jobject o, jstring s)
      {
          const char *str = env->GetStringUTFChars(s, 0);
          cout << "Hello! " << str << endl;
          env->ReleaseStringUTFChars(s, str);
      }
      
    9. There is no garbage collector in C++ world, so we need to do some cleanup. We save this as HelloWorld.cpp and compile it to generate shared library:
    10. g++ -shared -I/usr/local/jdk1.6.0_21/include -I/usr/local/jdk1.6.0_21/include/linux HelloWorld.cpp -o libHelloWorld.so
      !Attention: The path to your java include directory may be different.
    11. Run this example as following:
    12. java -Djava.library.path=. HelloWorld
      The result like this:
      Hello! Ryan Cho