2012년 1월 26일 목요일

Quick way to extract AMR-audio from Android 3GP files

출처 : http://android.amberfog.com/?p=181



Quick way to extract AMR-audio from Android 3GP files

If you need to record audio from microphone of your Android-device to a file, it is better to useMediaRecorder;. On the current Android platforms (we can be sure for 1.6, 1.5 and earlier versions) you can record only to 3GPP AMR-NB file, but not to the raw AMR. If you will try to use MediaRecorder.OutputFormat.RAW_AMR you will get native error that will not come to Java layer. We’re pretty sure that it will be fixed in the future, but what to do now if you need simple raw AMR-NB file without extra 3GP-container?
Simple Google search about how to extract raw AMR audio from 3GPP-file recorded by your Android return us a link to the isobox4j library that can convert between all kinds of 3gp, amr, mp4, etc. files. It is perfect, but it is 400Kb. Do you really need to have such huge library in your project only for converting one type of file to another?
I will show you the easier way that works fine only with Android 3GP-files. What you need to do – just remove 3GP-container and store AMR-data with appropriate header.
3GP-header fields are stored in 32-bits, big-endian format. So, you need correct functions to read 32-bits values from ByteArrayInputStream.
public long readUint32(ByteArrayInputStream bis) {
    long result = 0;
    result += ((long) readUInt16(bis)) << 16;
    result += readUInt16(bis);
    return result;
}
public int readUInt16(ByteArrayInputStream bis) {
    int result = 0;
    result += bis.read() << 8;
    result += bis.read();
    return result;
}
Now you need to skip 3gp-headers. First header is file header (‘ftyp’) and next is MediaData (‘mdat’). I’ve made it according to 3GP and Iso file structure specifications, using classes. Base Box contains two fields: size and type.
class Box {
    protected long size;
    protected long type;
 
    protected long boxSize = 0;
 
    public Box(ByteArrayInputStream bis) {
        size = readUint32(bis);
        boxSize += 4;
        type = readUint32(bis);
        boxSize += 4;
    }
}
Now classes for Filetype header and MediaData header:
class FileTypeBox extends Box {
    private static char[] HEADER_TYPE = {'f', 't', 'y', 'p'};
 
    protected long brand;
    protected long minorVersion;
    protected long[] compatibleBrands;
 
    public FileTypeBox(ByteArrayInputStream bis) {
        super(bis);
        brand = readUint32(bis);
        boxSize += 4;
        minorVersion = readUint32(bis);
        boxSize += 4;
        int remainSize = (int)(size - boxSize);
        if (remainSize > 0) {
            compatibleBrands = new long[remainSize / 4];
            for (int i = 0; i < compatibleBrands.length; i++) {
                compatibleBrands[i] = readUint32(bis);
            }
        }
    }
}
class MediaDataBox extends Box {
    private static char[] HEADER_TYPE = {'m', 'd', 'a', 't'};
 
    protected byte[] data;
 
    public MediaDataBox(ByteArrayInputStream bis) {
        super(bis);
        // you can read data[] here, but we need only size
    }
 
    public int getDataLength() {
        return (int)(size - boxSize);
    }
}
I add HEADER_TYPE to each Box-class. You can add checking that class field type has the appropriate HEADER_TYPE value just to be sure that you read and parse correct file.
So, we’re ready to convert 3GP to AMR. 3GP-structure is as follows:
1. FileType header
2. MediaDataHeader. Data inside it is raw AMR data.
3. MovieData box (‘moov’ type) that presents, but is empty and we don’t need to copy it to destination AMR-file.
Converting steps is as follows:
1. Skip FileType header
2. Skip MediaData Header size and type fields, get raw AMR data only.
3. Create new file
4. Add AMR-header magic number – “#!AMR\n” (according to RFC-3267)
5. Copy all raw AMR data to file.
That’s it!
// #!AMR\n
private static byte[] AMR_MAGIC_HEADER = {0x23, 0x21, 0x41, 0x4d, 0x52, 0x0a};
 
public byte[] convert3gpDataToAmr(byte[] data) {
    if (data == null) {
        return null;
    }
 
    ByteArrayInputStream bis = new ByteArrayInputStream(data);
    // read FileTypeHeader
    FileTypeBox ftypHeader = new FileTypeBox(bis);
    // You can check if it is correct here
    // read MediaDataHeader
    MediaDataBox mdatHeader = new MediaDataBox(bis);
    // You can check if it is correct here
    int rawAmrDataLength = mdatHeader.getDataLength();
    int fullAmrDataLength = AMR_MAGIC_HEADER.length + rawAmrDataLength;
    byte[] amrData = new byte[fullAmrDataLength];
    System.arraycopy(AMR_MAGIC_HEADER, 0, amrData, 0, AMR_MAGIC_HEADER.length);
    bis.read(amrData, AMR_MAGIC_HEADER.length, rawAmrDataLength);
    return amrData;
}
Enjoy!

2012년 1월 19일 목요일

port exosip to android

출처 : http://rxwen.wordpress.com/2010/12/20/port-exosip-to-android/


port exosip to android

Port exosip to android platform isn’t a difficult task because exosip doesn’t rely on any special system calls that aren’t available on android. It only requires osip to compile, which can also be easily ported.
As an example, I created two applications to run against exosip lib. One is a native application that can run in android shell, the other is an java application that interact with exosip through jni. The dependency relationship between modules is:
The diagram below depicts the organization of files. They’re organized this way so that they can be compiled through androidndk build system.
exosip_root (NDK_MODULE_PATH environment variable points here)
To comply with ndk build system’s requirements, we create a directory named jni under sip_jni and sip_exe module, and place actual source file there. The Android.mk and Application.mk (optional) are placed in the jni directory as well. Keeping files this way, we can issue ndk-build command right in sip_jni or sip_exe directories to compile applications.
Note that we don’t create jni directory for libosip and libexosip, their Android.mk is placed directly under libosip and libexosip directories. This is because they are dependent on by application modules. We don’t need compile them directly, instead, the build system will build them automatically while building applications. In order to help build system find libosip and libexosip, we must set NDK_MODULE_PATH environment variable to the directory that directly containing  libosip and libexosip. The build system will search for them based on directory name, so their names matter.
To port exosip to android, the essential task is to create theAndroid.mk file, which specifies source files, c flags, ld flags, dependent libraries. We define HAVE_TIME_H and HAVE_SYS_SELECT_H to compile exosip successfully. And we define ENABLE_TRACE and OSIP_MT to enable logging and multi-threading. In the last line, $(call import-module,libosip) tells the build system that exosip depends on osip, and all header files exported by osip (LOCAL_EXPORT_C_INCLUDES:=$(LOCAL_C_INCLUDES))  will be included by exosip automatically. 
import-module is a feature that isn’t available in the build system in android source tree. It enables us organizing our projects in arbitrary manner. For example, we can place libosip and libexosip directories in another directory. The build system can still find them as long as NDK_MODULE_PATH is set to the directory containing them. It’s much more flexible than specifying dependency relationship with relative path.
Sample:
The sample for this post is available at:
http://code.google.com/p/rxwen-blog-stuff/source/browse/trunk/android/exosip_sample/
It shows how to:
  1. use ndk build system
  2. use stl in c++ code
  3. create a very basic exosip application
  4. call native code from java
  5. call java code from native code

An basic introduction to SIP

출처 : http://rxwen.blogspot.com/2009/11/basic-introduction-to-sip.html


An basic introduction to SIP

What is SIP
SIP Protocol is one of widely used signaling protocol, with the main purpose of, as its name is abbreviated for, initialization session between several participants. Due to its nature, it's mainly used in VOIP, IM applications.

Basics of SIP
Because SIP is always mentioned together with other multimedia application protocols such as SDPRTP. They are so mixed up that easily confuse beginners, like me. To make things clear, it's necessary to know SIP has nothing to do with those protocols. It can be used independently with SDP and RTP. SIP is just being lucky to be employed to work with them in many situations.
In short, their relationship is:
SDP describes the capabilities and preferences of a participant. Usually, it's included in the payload of SIP message.
SIP is the message flow that enable two or more participants to negotiate, setup, modify and terminate a session.
RTP is the protocol for transferring audio or video data after the session has been established, it usually relies on the information contains in SDP.

SIP is a text based protocol with request/response transaction model, similar to HTTP. Unlike HTTP, whose request and response always comes in pairs, SIP's request and response doesn't hold a one on one relationship. A SIP transaction may have multiple request and multiple response.
The format of SIP message is similar to HTTP's too. A message is composed of a start-line(includes method, request-uri and sip-version), several headers and a message body.
Like SMTP, the recipient of a message doesn't have to be directly accessible to the sender. A message may hop several times before it reaches the destination with the help of Proxy Server.

To better understand SIP protocol, the document SIP Protocol Structure through an Example provides a thorough illustration and it's good material for getting start.

SIP Libraries
PJSIP is a powerful sip library implemented in c. Besides SIP, it also support SDP, and has strong multi-media processing capability. It exposes APIs at different layers so that users can pick either a convenient or flexible API set at their will. It's also a highly portable library can run many operating systems, including windows and linux. The best thing is it has complete document which is rare in open source project.
MJSIP is a java based sip library with SDP, RTP supporting. It's also a convenient and complete solution for building VOIP application. But I'd say the library still have room to improve the quality, for there are some apparent spelling mistakes.
OSIP is part of of gnu project, implemented in c. By following the "do one thing, and do it well" philosophy, it solely focus on SIP and is a light weight, fast library.

References:
http://www.ietf.org/rfc/rfc3261.txt
http://en.wikipedia.org/wiki/Session_Description_Protocol
http://en.wikipedia.org/wiki/Real-time_Transport_Protocol

2012년 1월 6일 금요일

JNI : Fields and Methods

출처 : http://java.sun.com/docs/books/jni/html/fldmeth.html



Chapter 4

Fields and Methods



Now that you know how the JNI lets native code access primitive types and reference types such as strings and arrays, the next step will be to learn how to interact with fields and methods in arbitrary objects. In addition to accessing fields, this includes making calls to methods implemented in the Java programming language from native code, commonly known as performing callbacks from native code.
We will begin by introducing the JNI functions that support field access and method callbacks. Later in this chapter we will discuss how to make such operations more efficient by using a simple but effective caching technique. In the last section, we will discuss the performance characteristics of calling native methods as well as accessing fields and calling methods from native code.

4.1 Accessing Fields

The Java programming language supports two kinds of fields. Each instance of a class has its own copy of the instance fields of the class, whereas all instances of a class share thestatic fields of the class.
The JNI provides functions that native code can use to get and set instance fields in objects and static fields in classes. Let us first look at an example program that illustrates how to access instance fields from a native method implementation.









 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");
     }
 }
The InstanceFieldAccess class defines an instance field s. The main method creates an object, sets the instance field, and then calls the native method InstanceFieldAccess.accessField. As we will see shortly, the native method prints out the existing value of the instance field and then sets the field to a new value. The program prints the field value again after the native method returns, demonstrating that the field value has indeed changed.
Here is the implementation of the InstanceFieldAccess.accessField native method.
 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);
 }
Running the InstanceFieldAccess class with the InstanceFieldAccess native library produces the following output:
 In C:
   c.s = "abc"
 In Java:
   c.s = "123"

4.1.1 Procedure for Accessing an Instance Field

To access an instance field, the native method follows a two-step process. First, it calls GetFieldID to obtain the field ID from the class reference, field name, and field descriptor:
 fid = (*env)->GetFieldID(env, cls, "s", "Ljava/lang/String;");
The example code obtains the class reference cls by calling GetObjectClass on the instance reference obj, which is passed as the second argument to the native method implementation.
Once you have obtained the field ID, you can pass the object reference and the field ID to the appropriate instance field access function:
 jstr = (*env)->GetObjectField(env, obj, fid);
Because strings and arrays are special kinds of objects, we use GetObjectField to access the instance field that is a string. Besides Get/SetObjectField, the JNI also supports other functions such as GetIntField and SetFloatField for accessing instance fields of primitive types.

4.1.2 Field Descriptors

You might have noticed that in the previous section we used a specially encoded C string "Ljava/lang/String;" to represent a field type in the Java programming language. These C strings are called JNI field descriptors.
The content of the string is determined by the declared type of the field. For example, you represent an int field with "I", a float field with "F", a double field with "D", a boolean field with"Z", and so on.
The descriptor for a reference type, such as java.lang.String, begins with the letter L, is followed by the JNI class descriptor (§3.3.5), and is terminated by a semicolon. The "."separators in fully qualified class names are changed to "/" in JNI class descriptors. Thus, you form the field descriptor for a field with type java.lang.String as follows:
 "Ljava/lang/String;"
Descriptors for array types consist of the "[" character, followed by the descriptor of the component type of the array. For example, "[I" is the descriptor for the int[] field type. Section 12.3.3 contains the details of field descriptors and their matching types in the Java programming language.
You can use the javap tool (shipped with JDK or Java 2 SDK releases) to generate the field descriptors from class files. Normally javap prints out the method and field types in a given class. If you specify the -s option (and the -p option for exposing private members), javap prints JNI descriptors instead:
 javap -s -p InstanceFieldAccess
This gives you output containing the JNI descriptors for the field s:
 ...
 s Ljava/lang/String;
 ...
Using the javap tool helps eliminate mistakes that can occur from deriving JNI descriptor strings by hand.

4.1.3 Accessing Static Fields

Accessing static fields is similar to accessing instance fields. Let us look at a minor variation of the InstanceFieldAccess example:

 class StaticFielcdAccess {
     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");
     }
 }
The StaticFieldAccess class contains a static integer field si. The Static-FieldAccess.main method creates an object, initializes the static field, and then calls the native methodStaticFieldAccess.accessField. As we will see shortly, the native method prints out the existing value of the static field and then sets the field to a new value. To verify that the field has indeed changed, the program prints the static field value again after the native method returns.
Here is the implementation of the StaticFieldAccess.accessField native method.
 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);
 }
Running the program with the native library produces the following output:
 In C:
   StaticFieldAccess.si = 100
 In Java:
   StaticFieldAccess.si = 200
There are two differences between how you access a static field and how you access an instance field:
  1. You call GetStaticFieldID for static fields, as opposed to GetFieldID for instance fields. GetStaticFieldID and GetFieldID have the same return type jfieldID.
  2. 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.

4.2 Calling Methods

There are several kinds of methods in the Java programming language. Instance methods must be invoked on a specific instance of a class, whereas static methods may be invoked independent of any instance. We will defer the discussion of constructors to the next section.
The JNI supports a complete set of functions that allow you to perform callbacks from native code. The example program below contains a native method that in turn calls an instance method implemented in the Java programming language.
 class InstanceMethodCall {
     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");
     }
 }


Here is the implementation of the native method:
 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("In C\n");
     (*env)->CallVoidMethod(env, obj, mid);
 }
Running the above program produces the following output:
 In C
 In Java

4.2.1 Calling Instance Methods

The Java_InstanceMethodCall_nativeMethod implementation illustrates the two steps required to call an instance method:
  • The native method first calls the JNI function GetMethodIDGetMethodID 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. At this point, an immediate return from the native method causes a NoSuchMethodError to be thrown in the code that called InstanceMethodCall.nativeMethod.
  • The native method then calls CallVoidMethodCallVoidMethod invokes an instance method that has the return type void. You pass the object, the method ID, and the actual arguments (though in the above example there are none) to CallVoidMethod.
Besides the CallVoidMethod function, the JNI also supports method invocation functions with other return types. For example, if the method you called back returned a value of type int, then your native method would use CallIntMethod. Similarly, you can use CallObjectMethod to call methods that return objects, which include java.lang.String instances and arrays.
You can use Call<Type>Method family of functions to invoke interface methods as well. You must derive the method ID from the interface type. The following code segment, for example, invokes the Runnable.run method on a java.lang.Thread instance:
 jobject thd = ...; /* a java.lang.Thread instance */
 jmethodID mid;
 jclass runnableIntf = 
     (*env)->FindClass(env, "java/lang/Runnable");
 if (runnableIntf == NULL) {
     ... /* error handling */
 }
 mid = (*env)->GetMethodID(env, runnableIntf, "run", "()V");
 if (mid == NULL) {
     ... /* error handling */
 }
 (*env)->CallVoidMethod(env, thd, mid);
 ... /* check for possible exceptions */
We have seen in Section 3.3.5 that the FindClass function returns a reference to a named class. Here we also use it to obtain a reference to a named interface.

4.2.2 Forming the Method Descriptor

The JNI uses descriptor strings to denote method types in a way similar to how it denotes field types. A method descriptor combines the argument types and the return type of a method. The argument types appear first and are surrounded by one pair of parentheses. Argument types are listed in the order in which they appear in the method declaration. There are no separators between multiple argument types. If a method takes no arguments, this is represented with an empty pair of parentheses. Place the method's return type immediately after the right closing parenthesis for the argument types.
For example, "(I)V" denotes a method that takes one argument of type int and has return type void. "()D" denotes a method that takes no arguments and returns a double. Do not let C function prototypes such as "int f(void)" mislead you to thinking that "(V)I" is a valid method descriptor. Use "()I" instead.
Method descriptors may involve class descriptors (§12.3.2). For example, the method:
 native private String getLine(String);
has the following descriptor:
 "(Ljava/lang/String;)Ljava/lang/String;"
Descriptors for array types begin with the "[" character, followed by the descriptor of the array element type. For example, the method descriptor of:
 public static void main(String[] args);
is as follows:
 "([Ljava/lang/String;)V"
Section 12.3.4 gives a complete description of how to form JNI method descriptors. You can use the javap tool to print out JNI method descriptors. For example, by running:
 javap -s -p InstanceMethodCall
you obtain the following output:
 ...
 private callback ()V
 public static main ([Ljava/lang/String;)V
 private native nativeMethod ()V
 ...
The -s flag informs javap to output JNI descriptor strings rather than types as they appear in the Java programming language. The -p flag causes javap to include information about the private members of the class in its output.

4.2.3 Calling Static Methods

The previous example demonstrates how native code calls an instance method. Similarly, you can perform callbacks to static methods from native code by following these 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: CallStaticVoidMethodCallStaticBooleanMethod, and so on.
There is a key difference between the functions that allow you to call static methods and the functions that allow you to call instance methods. The former takes a class reference as the second argument, whereas the latter takes an object reference as the second argument. For example, you pass the class reference to CallStaticVoidMethod, but pass an object reference to CallVoidMethod.
At the Java programming language level, you can invoke a static method f in class Cls using two alternative syntaxes: either Cls.f oobj.f where obj is an instance of Cls. (The latter is the recommended programming style, however.) In the JNI, you must always specify the class reference when issuing static method calls from native code.
Let us look at an example that makes a callback to a static method from native code. It is a slight variation of the earlier InstanceMethodCall example:
 class StaticMethodCall {
     private native void nativeMethod();
     private static void callback() {
         System.out.println("In Java");
     }
     public static void main(String args[]) {
         StaticMethodCall c = new StaticMethodCall();
         c.nativeMethod();
     }
     static {
         System.loadLibrary("StaticMethodCall");
     }
 }
Here is the implementation of the native method:
 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("In C\n");
     (*env)->CallStaticVoidMethod(env, cls, mid);
 }
Make sure that you pass cls (highlighted in bold), as opposed to obj, to CallStaticVoidMethod. Running the above program produces the following expected output:
 In C
 In Java

4.2.4 Calling Instance Methods of a Superclass

You can call instance methods which were defined in a superclass but that have been overridden in the class to which the object belongs. The JNI provides a set ofCallNonvirtual<Type>Method functions for this purpose. To call a instance method defined in a superclass, you do the following:
  • Obtain the method ID from a reference to the superclass using GetMethodID, as opposed to GetStaticMethodID.
  • Pass the object, superclass, method ID, and arguments to one of the family of nonvirtual invocation functions, such as CallNonvirtualVoidMethodCallNonvirtualBooleanMethod, and so on.
It is relatively rare that you will need to invoke the instance methods of a superclass. This facility is similar to calling an overridden superclass method, say f, using the following construct in the Java programming language:
 super.f();
CallNonvirtualVoidMethod can also be used to invoke constructors, as the next section will illustrate.

4.3 Invoking Constructors

In the JNI, constructors may be invoked following steps similar to those used for calling instance methods. To obtain the method ID of a constructor, pass "<init>" as the method name and "V" as the return type in the method descriptor. You can then invoke the constructor by passing the method ID to JNI functions such as NewObject. 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;
 }
This function is complex enough to deserve careful explanation. First, FindClass returns a reference to the java.lang.String class. Next, GetMethodID returns the method ID for the string constructor, String(char[] chars). We then call NewCharArray to allocate a character array that holds all the string elements. The JNI function NewObject invokes the constructor specified by the method ID. The NewObject function takes as arguments the reference to the class to be constructed, the method ID of the constructor, and the arguments that need to be passed to the constructor.
The DeleteLocalRef call allows the virtual machine to free the resources used by local references elemArr and stringClass. Section 5.2.1 will provide a detailed description of when and why you should call DeleteLocalRef.
Strings are objects. This example highlights the point further. The example also leads to a question, however. Given that we can implement equivalent functionality using other JNI functions, why does the JNI provide built-in functions such as NewString? The reason is that the built-in string functions are far more efficient than calling the java.lang.String API from native code. String is the most frequently used type of objects, one that deserves special support in the JNI.
It is also possible to invoke constructors using the CallNonvirtualVoidMethod function. In this case, the native code must first create an uninitialized object by calling the AllocObjectfunction. The single NewObject call above:
 result = (*env)->NewObject(env, stringClass, cid, elemArr);
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, and must be used with care so that a constructor is called at most once on each object. The native code should not invoke a constructor on the same object multiple times.
Occasionally you may find it useful to allocate an uninitialized object first and call the constructor sometime later. In most cases, however, you should use NewObject and avoid the more error-prone AllocObject/CallNonvirtualVoidMethod pair.

4.4 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. In this section, we introduce a technique that can be used to reduce this overhead.
The idea is to compute field and method IDs and cache them for repeated uses later. There are two ways to cache field and method IDs, depending upon whether caching is performed at the point of use of the field or method ID, or in the static initializer of the class that defines the field or method.

4.4.1 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. The following implementation of theJava_InstanceFieldAccess_accessField function caches the field ID in static variables so that it need not be recomputed upon each invocation of the InstanceFieldAccess.accessFieldmethod.

 JNIEXPORT void JNICALL 
 Java_InstanceFieldAccess_accessField(JNIEnv *env, jobject obj)
 {
     static jfieldID fid_s = NULL; /* cached field ID for s */
 
     jclass cls = (*env)->GetObjectClass(env, obj);
     jstring jstr;
     const char *str;
 
     if (fid_s == NULL) {
         fid_s = (*env)->GetFieldID(env, cls, "s", 
                                    "Ljava/lang/String;");
         if (fid_s == NULL) {
             return; /* exception already thrown */
         }
     }
 
     printf("In C:\n");
 
     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 highlighted static variable fid_s stores the precomputed field ID for InstanceFieldAccess.s. The static variable is initialized to NULL. 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.
You may notice that there is an obvious race condition in the above code. Multiple threads may call the InstanceFieldAccess.accessField method at the same time and compute the same field ID concurrently. One thread may overwrite the static variable fid_s computed by another thread. Luckily, although this race condition leads to duplicated work in multiple threads, it is otherwise harmless. The field IDs computed by multiple threads for the same field in the same class will necessarily be the same.
Following the same idea, we may also cache the method ID for the java.lang.String constructor in the earlier MyNewString example:
 jstring
 MyNewString(JNIEnv *env, jchar *chars, jint len)
 {
     jclass stringClass;
     jcharArray elemArr;
     static jmethodID cid = NULL;
     jstring result;
 
     stringClass = (*env)->FindClass(env, "java/lang/String");
     if (stringClass == NULL) {
         return NULL; /* exception thrown */
     }
 
     /* Note that cid is a static variable */
     if (cid == NULL) {
         /* Get the method ID for the String 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;
 }
We compute the method ID for the java.lang.String constructor when MyNewString is called for the first time. The highlighted static variable cid caches the result.

4.4.2 Caching in the Defining Class's Initializer

When we cache a field or method ID at the point of use we must introduce a check to detect whether the IDs have already been cached. Not only does this approach incur a small performance impact on the "fast path" when the IDs have already been cached, but it could lead to duplication of caching and checking as well. For example, if multiple native methods all require access to the same field, then they all need a check to compute and cache the corresponding field ID.
In many situations 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. The virtual machine always executes the static initializer of a class before it invokes any of the methods in that class. Thus 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.
For example, to cache the method ID for InstanceMethodCall.callback we introduce a new native method initIDs, called from the static initializer of the InstanceMethodCall class:
 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();
     }
 }
Compared to the original code in Section 4.2, the above program contains two extra lines (highlighted in bold font). The implementation of initIDs simply computes and caches the method ID for InstanceMethodCall.callback:
 jmethodID MID_InstanceMethodCall_callback;
 
 JNIEXPORT void JNICALL 
 Java_InstanceMethodCall_initIDs(JNIEnv *env, jclass cls)
 {
     MID_InstanceMethodCall_callback =
         (*env)->GetMethodID(env, cls, "callback", "()V");
 }
The virtual machine runs the static initializer, and in turn calls the initIDs method, before executing any other methods (such as nativeMethod or main) 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:
 JNIEXPORT void JNICALL 
 Java_InstanceMethodCall_nativeMethod(JNIEnv *env, jobject obj)
 {
     printf("In C\n");
     (*env)->CallVoidMethod(env, obj,
                            MID_InstanceMethodCall_callback);
 }

4.4.3 Comparison between the Two Approaches to Caching IDs

Caching IDs at the point of use is the reasonable solution if the JNI programmer does not have control over the source of the class that defines the field or method. For example, in the MyNewString example, we cannot inject a custom initIDs native method into the java.lang.String class in order to precompute and cache the method ID for the java.lang.Stringconstructor.
Caching at the point of use has a number of disadvantages when compared with caching in the static initializer of the defining class.
  • As explained before, caching at the point of use requires a check in the execution fast path and may also require duplicated checks and initialization of the same field or method ID.
  • Method and field IDs are only valid until the class is unloaded. If you cache field and method IDs at the point of use you must make sure that the defining class will not be unloaded and reloaded as long as the native code still relies on the value of the cached ID. (The next chapter will show how you can keep a class from being unloaded by creating a reference to that class using the JNI.) On the other hand, if 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.
Thus, where feasible, it is preferable to cache field and method IDs in the static initializer of their defining classes.

4.5 Performance of JNI Field and Method Operations

After learning how to cache field and method IDs to enhance performance, you might wonder: What are the performance characteristics of accessing fields and calling methods using the JNI? How does the cost of performing a callback from native code (a native/Java callback) compare with the cost of calling a native method (a Java/native call), and with the cost of calling a regular method (a Java/Java call)?
The answer to this question no doubt depends on how efficiently the underlying virtual machine implements the JNI. It is thus impossible to give an exact account of performance characteristics that is guaranteed to apply to a wide variety of virtual machine implementations. Instead, we will analyze the inherent cost of native method calls and JNI field and method operations and provide a general performance guideline for JNI programmers and implementors.
Let us start by comparing the cost of Java/native calls with the cost of Java/Java calls. 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.
We estimate that a typical virtual machine may execute a Java/native call roughly two to three times slower than it executes a Java/Java call. Because a Java/Java call takes just a few cycles, the added overhead will be negligible unless the native method performs trivial operations. It is also possible to build virtual machine implementations with Java/native call performance close or equal to that of Java/Java calls. (Such virtual machine implementations, for example, may adopt the JNI calling convention as the internal Java/Java calling convention.)
The performance characteristics of a native/Java callback is technically similar to a Java/native call. In theory, the overhead of native/Java callbacks could also be within two to three times of Java/Java calls. In practice, however, native/Java callbacks are relatively infrequent. Virtual machine implementations do not usually optimize the performance of callbacks. At the time of this writing many production virtual machine implementations are such that the overhead of a native/Java callback can be as much as ten times higher than a Java/Java call.
The overhead of field access using the JNI lies in the cost of calling through the JNIEnv. Rather than directly dereferencing objects, the native code has to perform a C function call which in turn dereferences the object. The function call is necessary because it isolates the native code from the internal object representation maintained by the virtual machine implementation. The JNI field access overhead is typically negligible because a function call takes only a few cycles.