This is a JNI tutorial that I (SteveO) put together to help anyone write The JNI (Java Native Interface) style natives.
Also see the sun JNI Reference documentation
There is also a lovely sun tutorial
First, if you do not, there is no guarantee that your natives will work with version 2.0 of the JVM. Version 1.1 is a transition version for native programming. I have heard rumors that unless you use JNI, your natives will not work with a 2.x version JVM. The JVM from Microsoft will not work with JNI. They have splintered off with their own JVM which uses a different flavor of natives. Microsoft's solution is RNI: Raw Native Interface. If you want natives to work with the MS JVM, you will have to maintain a different source for the RNI natives. If you plan on distributing libraries, you should write to the JNI and the RNI interfaces.
Declare the native in your java program:
public native int add(int a, int b);
javac AddMe
javah -jni file
JNIEXPORT jint JNICALL Java_AddMe_AddMeNtv (JNIEnv *, jobject, jint, jint);is the call generate by javah (see AddMe.h), and that must be your function declaration in your C code. See the sun tutorial for more information.
C code:
HArrayOfChar *charArray;
long length = unilen(cAbbreviatedName);
charArray = makeCharArray(cAbbreviatedName, length);
execute_java_dynamic_method (
EE(),
(HObject *) abbreviatedName,
"append",
"([C)Ljava/lang/StringBuffer;",
charArray);
Using JNI:
JNIEXPORT jint JNICALL
Java_COM_novell_nsi_libsWrapper_BinderyJNI_NWScanObject /* modified for example */
(
JNIEnv *env,
jclass obj,
jobject objName, // out, optional, StringBuffer
)
{
nstr8 cObjectName [MAX_BINDERY_OBJECT_NAME_LEN];
jstring javaString;
jclass cls;
jmethodID mid;
.
.
.
Some code to get cObjectName
javaString = (jstring)(*env)->NewStringUTF(env, (const char *)cObjectName);
cls = (*env)->GetObjectClass(env, objName);
mid = (*env)->GetMethodID(env, cls, "append","(Ljava/lang/String;)Ljava/lang/StringBuffer;");
if (0 == mid)
{
printf("GetMethodID returned 0\n");
return(-1);
}
(*env)->CallObjectMethod(env, objName, mid, javaString);
The 1.0 old way was to use the call:
Example (new way with localization):
CFunction(JNIEnv *env,
jclass cls,
jstring jString) // this guy comes in
{
unicode *uStr;
pnstr cLocalizedStr;
size_t uLength;
cLocalizedStr =(pnstr) malloc(((*env)->GetStringLength(env,jString)+1)*2);
uStr = (unicode *)(*env)->GetStringChars(env,jString,0); //stays in unicode
ccode = NWUnicodeToLocal(_hUnicodeToLocal,
((pnuint8) cLocalizedStr), // this will contain our new string
((*env)->GetStringLength(env,jString)+1),
uStr,
0xFF, // strNoMap
&uLength
);
if(N_SUCCESS != ccode)
{
return(ccode);
}
Example (jni way without localization):
uStr = (unicode *)(*env)->GetStringChars(env,jString,0); //stays in unicode
Example Cstring, you are sure is not unicode ever.
Str = (*env)->GetStringUTFChars(env,jString,0); //regular C string
This is so simple it hardly deserves mention.
In java:
The native declaration:
public native int passInt(int intToPass);
The java call
int j = 1;
passInt(j);
The native handling in C
JNIEXPORT jint JNICALL
Java_passInt(jint thePassedInt)
{
//jint in C is a signed 32 bit integer;
printf("the int is %d",thePassedInt);
//There is no way to change the java variable directly.
thePassedInt = 5; // this gets lost after the return.
return(thePassedInt); // you could return it,
// But what if you have more than one to return?
// Then you must pass in (Integer?) objects and set the values
// through CallObjectMethod() or SetIntField().
}
cls = (*env)->GetObjectClass(env, objID);
mid = (*env)->GetMethodID(env, cls, "setValue","(I)V");
if (0 == mid)
{
printf("GetMethodID returned 0\n");
return(-1);
}
(*env)->CallVoidMethod(env, objID, mid, cObjectID);
name = (*env)->NewStringUTF(env,entryInfo.entryName);
if (name == NULL)
{
printf("NewStringUTF returned NULL\n");
return (-1);
}
fid = (*env)->GetFieldID(env,clazz,"entryName","Ljava/lang/String;");
(*env)->SetObjectField(env,info,fid,name);
In java you need an object to represent the structure.
Then you pass the java object into the native.
From C, access the class directly with calls to SetTypeField, where type
is a java type.
In the following example, info is the java type which represents the
C structure testInfo.
struct info{
int index;
int space;
int count;
int key;
int nameLength;
char name[30];
}testInfo;
Here is the java class to represent testInfo
public class EntryInformation
{
protected int index;
protected int space;
protected int count;
protected int key;
protected int nameLength;
protected String name;
}
Here is the native call.
JNIEXPORT jint JNICALL
Java_FillCStruct
(
JNIEnv *env,
jclass obj,
jobject info // EntryInformation object instantiation
)
{
testInfo entryInfo;
jclass clazz;
jfieldID fid;
jmethodID mid;
GetInfo(entryInfo); // fills in the entryInfo
clazz = (*env)->GetObjectClass(env, info);
if (0 == clazz)
{
printf("GetObjectClass returned 0\n");
return(-1);
}
fid = (*env)->GetFieldID(env,clazz,"index","I");
// This next line is where the power is hidden. Directly change
// even private fields within java objects. Nasty!
(*env)->SetIntField(env,info,fid,testInfo.index);
fid = (*env)->GetFieldID(env,clazz,"space","I");
(*env)->SetIntField(env,info,fid,testInfo.space);
fid = (*env)->GetFieldID(env,clazz,"count","I");
(*env)->SetIntField(env,info,fid,testInfo.count);
fid = (*env)->GetFieldID(env,clazz,"key","I");
(*env)->SetIntField(env,info,fid,testInfo.key);
fid = (*env)->GetFieldID(env,clazz,"nameLength","I");
(*env)->SetIntField(env,info,fid,testInfo.nameLength);
name = (*env)->NewStringUTF(env,testInfo.name);
if (name == NULL)
{
clazz = (*env)->FindClass(env,"java/lang/OutOfMemoryError");
(*env)->ThrowNew(env,clazz,NULL);
return (-1);
}
fid = (*env)->GetFieldID(env,clazz,"name","Ljava/lang/String;");
(*env)->SetObjectField(env,info,fid,name);
}
Making Java Strings in C
non-jni code: Hjava_lang_String *name; name = makeJavaString (cName, strlen (cName)); jni code: name = (*env)->NewStringUTF(env,cName); if (name == NULL) { clazz = (*env)->FindClass(env,"java/lang/OutOfMemoryError"); (*env)->ThrowNew(env,clazz,NULL); return (-1); }
Passing a Single String Array Element from C to Javanon-jni code: Hjava_lang_String *javaVolName; unhand (volName)->body[0] = (HString *) javaVolName; jni code: jobjectArray volName; jstring javaVolName; javaVolName = (*env)->NewStringUTF(env, cVolNameStr); (*env)->SetObjectArrayElement(env,volName,0,javaVolName);
Passing a Single Int Array Element from Java to C
non-jni code: cInt = unhand (volNumber)->body[0]; jni code: jint *arr; arr = (*env)->GetIntArrayElements(env, javaIntArr,0); cInt = (nuint8) arr[0]; (*env)->ReleaseIntArrayElements(env, javaIntArr, arr, 0);
Passing a Single Int Array Element from C to Java
non-jni code: unhand (volNumber)->body[0] = cInt; jni code: . . . jint jIntTemp[1]; // SetIntArrayRegion expects a jintArray coming in jIntTemp[0] = searchSequence.searchDirNumber; (*env)->SetIntArrayRegion(env, javaIntArr,0,1,jIntTemp); . . .
Newing up a Java object in C
cls = (*env)->FindClass(env, "com/novell/java/lang/IntegerBuffer"); mid = (*env)->GetMethodID (env, cls, "init","(I)V"); (put init inbetween greater than, less than brackets) object = (*env)->NewObject(env, cls, mid, (jint) cValue);