|
Training Index
[<<BACK] [CONTENTS] [NEXT>>]
This section explains how to pass string and array data
between a program written in the Java programming language and
other languages.
Passing Strings
The String object in the Java language, which is
represented as jstring in Java Native Interface (JNI), is
a 16 bit unicode string. In C a
string is by default constructed from 8 bit characters. So, to
access a Java language String object passed to a
C or C++ function or return a C or C++ string to a Java language
method, you need to use JNI conversion functions in your native method
implementation.
The GetStringUTFChars function retrieves 8-bit
characters from a 16-bit jstring using the
Unicode Transformation Format
(UTF). UTF represents Unicode as a string of 8 or 16 bit characters
without losing any information. The third parameter
GetStringUTFChars results the result
JNI_TRUE if it made a local copy of the
jstring or JNI_FALSE otherwise.
C Version:
(*env)->GetStringUTFChars(env, name, iscopy)
C++ Version:
env->GetStringUTFChars(name, iscopy)
|
The following C JNI function converts an array
of C characters to a jstring:
(*env)->NewStringUTF(env, lastfile)
The example below converts the lastfile[80] C
character array to a jstring, which is returned
to the calling Java language method:
static char lastfile[80];
JNIEXPORT jstring JNICALL Java_ReadFile_lastFile
(JNIEnv *env, jobject jobj) {
return((*env)->NewStringUTF(env, lastfile));
}
|
To let the Java1 virtual machine know you are finished with the UTF
representation, call the ReleaseStringUTFChars conversion
function as shown below. The second argument is the original jstring
value used to construct the UTF representation, and the third argument is the
reference to the local representation of that String.
(*env)->ReleaseStringUTFChars(env, name, mfile);
If your native code can work with Unicode, without needing the intermediate
UTF representation, call the GetStringChars function
to retrieve the unicode string, and release the reference with
a call to ReleaseStringChars:
JNIEXPORT jbyteArray JNICALL Java_ReadFile_loadFile
(JNIEnv * env, jobject jobj, jstring name) {
caddr_t m;
jbyteArray jb;
struct stat finfo;
jboolean iscopy;
const jchar *mfile = (*env)->GetStringChars(env,
name, &iscopy);
//...
(*env)->ReleaseStringChars(env, name, mfile);
|
Passing Arrays
In the example presented in the last section, the
loadFile native method
returns the contents of a file in a byte array, which is a
primitive type in the Java programming language. You can retrieve and create
primitive types in the Java language by calling the appropriate
TypeArray function.
For example, to create a new array of floats, call NewFloatArray,
or to create a new array of bytes, call NewByteArray. This
naming scheme extends to retrieving elements from, adding elements to, and
changing elements in the array. To get a new array of bytes, call
GetByteArrayElements. To add elements to or change elements in
the array, call Set<type>ArrayElements.
The GetByteArrayElements function affects the entire array. To
work on a portion of the array, call GetByteArrayRegion instead.
There is only a Set<type>ArrayRegion function for
changing array elements.
However the region could be of size 1, which is equivalent
to the non-existent Set<type>ArrayElements.
Native Code Type | Functions used |
| jboolean | NewBooleanArray |
| GetBooleanArrayElements |
| GetBooleanArrayRegion/SetBooleanArrayRegion |
| ReleaseBooleanArrayElements |
| jbyte | NewByteArray |
| GetByteArrayElements |
| GetByteArrayRegion/SetByteArrayRegion |
| ReleaseByteArrayElements |
| jchar | NewCharArray |
| GetCharArrayElements |
| GetCharArrayRegion/SetCharArrayRegion |
| ReleaseCharArrayElements |
| jdouble | NewDoubleArray |
| GetDoubleArrayElements |
| GetDoubleArrayRegion/SetDoubleArrayRegion |
| ReleaseDoubleArrayElements |
| jfloat | NewFloatArray |
| GetFloatArrayElements |
| GetFloatArrayRegion/SetFloatArrayRegion |
|
ReleaseFloatArrayElements |
| jint | NewIntArray |
| GetIntArrayElements |
| GetIntArrayRegion/SetIntArrayRegion |
| ReleaseIntArrayElements |
| jlong | NewLongArray |
| GetLongArrayElements |
| GetLongArrayRegion/SetLongArrayRegion |
| ReleaseLongArrayElements |
| jobject | NewObjectArray |
| GetObjectArrayElement/SetObjectArrayElement |
| jshort | NewShortArray |
| GetShortArrayElements |
| GetShortArrayRegion/SetShortArrayRegion |
| ReleaseShortArrayElements |
In the loadFile native method from the example in the
previous section, the entire array is updated by
specifying a region that is the size of the file being read in:
jbyteArray jb;
jb=(*env)->NewByteArray(env, finfo.st_size);
(*env)->SetByteArrayRegion(env, jb, 0,
finfo.st_size, (jbyte *)m);
close(fd);
|
The array is returned to the calling Java language
method, which in turn, garbage collects the reference to the array
when it is no longer used. The array can be explicitly freed with
the following call.
(*env)-> ReleaseByteArrayElements(env, jb,
(jbyte *)m, 0);
The last argument to the ReleaseByteArrayElements function
above can have the following values:
- 0: Updates to the array from within the C code are reflected in the
Java language copy.
JNI_COMMIT: The Java language copy is
updated, but the local jbyteArray is not freed.
- JNI_ABORT: Changes are not copied back, but the
jbyteArray
is freed. The value is used only if the array is obtained with a get mode of
JNI_TRUE meaning the array is a copy.
Pinning Array
When retrieving an array, you can specify if this is a copy
(JNI_TRUE) or a reference to the array residing in
your Java language program (JNI_FALSE).
If you use a reference to the array, you will want the array to stay
where it is in the Java heap and not get moved by the garbage
collector when it compacts heap memory. To prevent the array references
from being moved, the Java virtual machine pins the array into
memory. Pinning the array ensures that when the array is released,
the correct elements are updated in the Java VM.
In the loadfile native method example from the
previous section, the array is not explicitly released. One way
to ensure the array is garbage collected when it is no longer needed
is to call a Java language method, pass the byte array instead, and
then free the local array copy.
This technique is shown in the section on Multi-Dimensional
Arrays.
Object Arrays
You can store any Java language object in an array with the
NewObjectArray and SetObjectArrayElement
function calls. The main difference between an object array and an
array of primitive types
is that when constructing a jobjectarray type,
the Java language class is used as a parameter.
This next C++ example shows how to call NewObjectArray
to create an array of String objects. The size of the
array is set to five, the class definition is returned from a call
to FindClass, and the elements of the array are initialized
with an empty string. The elements of the array are updated by calling
SetObjectArrayElement
with the position and value to put in the array.
#include <jni.h>
#include "ArrayHandler.h"
JNIEXPORT jobjectArray JNICALL
Java_ArrayHandler_returnArray
(JNIEnv *env, jobject jobj){
jobjectArray ret;
int i;
char *message[5]= {"first",
"second",
"third",
"fourth",
"fifth"};
ret= (jobjectArray)env->NewObjectArray(5,
env->FindClass("java/lang/String"),
env->NewStringUTF(""));
for(i=0;i<5;i++) {
env->SetObjectArrayElement(
ret,i,env->NewStringUTF(env, message[i]));
}
return(ret);
}
|
The Java class that calls this native method is as follows:
public class ArrayHandler {
public native String[] returnArray();
static{
System.loadLibrary("nativelib");
}
public static void main(String args[]) {
String ar[];
ArrayHandler ah= new ArrayHandler();
ar = ah.returnArray();
for (int i=0; i<5; i++) {
System.out.println("array element"+i+
"=" + ar[i]);
}
}
}
|
Multi-Dimensional Arrays
You might need to call existing numerical and mathematical
libraries such as the linear algebra library CLAPACK/LAPACK
or other matrix crunching programs from your Java language
program using native methods.
Many of these libraries and programs use two-dimensional
and higher order arrays.
In the Java programming language, any array that has more than
one dimension is treated as an array of arrays.
For example, a two-dimensional integer array is handled as an array of
integer arrays. The array is read horizontally, or what is
also termed as row order.
Other languages such as FORTRAN use column ordering so extra care is needed
if your program hands a Java language array to a FORTRAN function. Also,
the array elements in an application written in the Java programming
language are not guaranteed to be contigous in memory. Some numerical
libraries use the knowledge that the array elements are stored next to
each other in memory to perform speed optimizations, so you might need
to make an additional local copy of the array to pass to those functions.
The next example passes a two-dimensional array to a native method which
then extracts the elements, performs a calculation, and calls a Java
language method to return the results.
The array is passed as an object array that contains an array of
jints. The individual elements are extracted by first
retrieving a jintArray instance from the object array by
calling GetObjectArrayElement, and then extracting the elements
from the jintArray row.
The example uses a fixed size
matrix. If you do not know the size of the array being used, the
GetArrayLength(array) function returns the size of the outermost
array. You will need to call the GetArrayLength(array) function
on each dimension of the array to discover the total size of the array.
The new array sent back to the program written in the Java langauge
is built in reverse.
First, a jintArray instance is created and that instance
is set in the object array by calling SetObjectArrayElement.
public class ArrayManipulation {
private int arrayResults[][];
Boolean lock=new Boolean(true);
int arraySize=-1;
public native void manipulateArray(
int[][] multiplier, Boolean lock);
static{
System.loadLibrary("nativelib");
}
public void sendArrayResults(int results[][]) {
arraySize=results.length;
arrayResults=new int[results.length][];
System.arraycopy(results,0,arrayResults,
0,arraySize);
}
public void displayArray() {
for (int i=0; i<arraySize; i++) {
for(int j=0; j <arrayResults[i].length;j++) {
System.out.println("array element "+i+","+j+
"= " + arrayResults[i][j]);
}
}
}
public static void main(String args[]) {
int[][] ar = new int[3][3];
int count=3;
for(int i=0;i<3;i++) {
for(int j=0;j<3;j++) {
ar[i][j]=count;
}
count++;
}
ArrayManipulation am= new ArrayManipulation();
am.manipulateArray(ar, am.lock);
am.displayArray();
}
}
#include <jni.h>
#include <iostream.h>
#include "ArrayManipulation.h"
JNIEXPORT void
JNICALL Java_ArrayManipulation_manipulateArray
(JNIEnv *env, jobject jobj, jobjectArray elements,
jobject lock){
jobjectArray ret;
int i,j;
jint arraysize;
int asize;
jclass cls;
jmethodID mid;
jfieldID fid;
long localArrayCopy[3][3];
long localMatrix[3]={4,4,4};
for(i=0; i<3; i++) {
jintArray oneDim=
(jintArray)env->GetObjectArrayElement(
elements, i);
jint *element=env->GetIntArrayElements(oneDim, 0);
for(j=0; j<3; j++) {
localArrayCopy[i][j]= element[j];
}
}
// With the C++ copy of the array,
// process the array with LAPACK, BLAS, etc.
for (i=0;i<3;i++) {
for (j=0; j<3 ; j++) {
localArrayCopy[i][j]=
localArrayCopy[i][j]*localMatrix[i];
}
}
// Create array to send back
jintArray row= (jintArray)env->NewIntArray(3);
ret=(jobjectArray)env->NewObjectArray(
3, env->GetObjectClass(row), 0);
for(i=0;i<3;i++) {
row= (jintArray)env->NewIntArray(3);
env->SetIntArrayRegion((jintArray)row,(
jsize)0,3,(jint *)localArrayCopy[i]);
env->SetObjectArrayElement(ret,i,row);
}
cls=env->GetObjectClass(jobj);
mid=env->GetMethodID(cls, "sendArrayResults",
"([[I)V");
if (mid == 0) {
cout <<"Can't find method sendArrayResults";
return;
}
env->ExceptionClear();
env->MonitorEnter(lock);
env->CallVoidMethod(jobj, mid, ret);
env->MonitorExit(lock);
if(env->ExceptionOccurred()) {
cout << "error occured copying array back" << endl;
env->ExceptionDescribe();
env->ExceptionClear();
}
fid=env->GetFieldID(cls, "arraySize", "I");
if (fid == 0) {
cout <<"Can't find field arraySize";
return;
}
asize=env->GetIntField(jobj,fid);
if(!env->ExceptionOccurred()) {
cout<< "Java array size=" << asize << endl;
} else {
env->ExceptionClear();
}
return;
}
|
_______
1 As used on this web site,
the terms "Java virtual
machine" or "JVM" mean a virtual machine
for the Java platform.
[TOP]
|
|