Simple JNI Program using Borland

JNI allows one to write native methods to handle situations when an application cannot be written entirely in the Java programming language, e.g. when the standard Java class library does not support the platform-specific features or program library.

It can be used to interface with code written in other languages, such as C and C++. It is also used for time-critical calculations or operations like solving complicated mathematical equations, since native code can be faster than JVM code.

How the JNI works

In the JNI framework, native functions are implemented in separate .c or .cpp files. (C++ provides a slightly cleaner interface with JNI.) When the JVM invokes the function, it passes a JNIEnv pointer, a jobject pointer, and any Java arguments declared by the Java method. A JNI function may look like this:

1

2

3

4

JNIEXPORT voidJNICALL Java_ClassName_MethodName

(JNIEnv *env,jobject obj){

/*Implement Native Method Here*/

}

The env pointer is a structure that contains the interface to the JVM. It includes all of the functions necessary to interact with the JVM and to work with Java objects. Example JNI functions are converting native arrays to/from Java arrays, converting native strings to/from Java strings, instantiating objects, throwing exceptions, etc. Basically, anything that Java code can do can be done using JNIEnv, albeit with considerably less ease.
For example, the following converts a Java string to a native string:

Note that C++ JNI code is syntactically slightly cleaner than C JNI code because like Java, C++ uses object method invocation semantics. That means that in C, the env parameter is dereferenced using (*env)->; and env has to be explicitly passed to JNIEnv methods. In C++, the env parameter is dereferenced using env-> and the env parameter is implicitly passed as part of the object method invocation semantics.

Native data types can be mapped to/from Java data types. For compound types such as objects, arrays and strings the native code must explicitly convert the data by calling methods in the JNIEnv.

Mapping types

The following table shows the mapping of types between Java and native code.

In addition, the signature L fully-qualified-class ; would mean the class uniquely specified by that name; e.g., the signature Ljava/lang/String; refers to the class java.lang.String. Also, prefixing [ to the signature makes the array of that type; for example, [I means the int array type.

Here, these types are interchangeable. You can use jint where you normally use an int, and vice-versa, without any typecasting required.

However, mapping between Java Strings and arrays to native strings and arrays is different. If you use a jstring in where a char * would be, your code could crash the JVM.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

// !!! Incorrect !!! //

JNIEXPORT voidJNICALL Java_ClassName_MethodName

(JNIEnv *env,jobject obj,jstring javaString)

{

printf("%s",javaString);

}

// Correct //

JNIEXPORT voidJNICALL Java_ClassName_MethodName

(JNIEnv *env,jobject obj,jstring javaString)

{

//Get the native string from javaString

constchar*nativeString=env->GetStringUTFChars(javaString,0);

printf("%s",nativeString);

//DON'T FORGET THIS LINE!!!

env->ReleaseStringUTFChars(javaString,nativeString);

}

This is similar with Java arrays, as illustrated in the example below that takes the sum of all the elements in an array.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

// !!! Incorrect !!! //

JNIEXPORT jint JNICALL

Java_IntArray_sumArray(JNIEnv *env,jobject obj,jintArray arr)

{

inti,sum=0;

for(i=0;i<10;i++){

sum+=arr[i];

}

returnsum;

}

// Correct //

JNIEXPORT jint JNICALL

Java_IntArray_sumArray(JNIEnv *env,jobject obj,jintArray arr)

{

jint buf[10];

jinti,sum=0;

env->GetIntArrayRegion(arr,0,10,buf);

for(i=0;i<10;i++){

sum+=buf[i];

}

returnsum;

}

Of course, there is much more to it than this. Look for links below for more information.

JNIEnv*

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. This JNI interface pointer can be stored, but remains valid only in the current thread. Other threads must first call AttachCurrentThread() to attach themselves to the VM and obtain a JNI interface pointer. Once attached, a native thread works like a regular Java thread running within a native method. The native thread remains attached to the VM until it calls DetachCurrentThread() to detach itself.
To attach to the current thread and get a JNI interface pointer:

1

2

JNIEnv *env;

(*g_vm)->AttachCurrentThread(g_vm,(void**)&env,NULL);

To detach from the current thread:

1

(*g_vm)->DetachCurrentThread(g_vm);

Compilation using Borland

This is a tutorial which told you, how to use JNI.
Below are the steps to use JNI Simple Hello world Program.

You have to install the Borland C++ Compiler(C++ Compiler 5.5). It can be downloaded from Borland C++ Compiler.

From a laptop to a full organization, Ganesh has traveled a tough road. A commando, armed with the latest tech & a huge warehouse of knowledge, he leads by example & is here for the long haul. His strong belief in giving back more has borne rich fruits, both for his employees & clients.