Original URL: http://blog.csdn.net/linweig/article/details/5203716
This article describes how to pass parameters and return values in JNI programming.
The first thing to emphasize is that the native method can not only pass Java's basic types to parameters, but also pass more complex types, such as String, array, or even custom classes. All this can be found in the jni.h of the answer.
1. Delivery of Java Basic types
People who have used Java know that the basic types in Java include boolean,byte,char,short,int,long,float,double, if you use these types of native method parameters, when you pass Javah- When the. h file is generated by JNI, just look at the generated. h file and it will be clear that the types that correspond to each type are jboolean,jbyte,jchar,jshort,jint,jlong,jfloat,jdouble. Almost all of these types can be used as a corresponding C + + type, so there's nothing to say.
2. Passing the string parameter
Java string and C + + string is not equal, so it is cumbersome to deal with. Let's look at an example,
Class Prompt {
Native method, 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");
}
}
In this example, we are going to implement a native method
String GetLine (String prompt);
Reads a string parameter and returns a string value.
The header files obtained by executing the javah-jni are like this
#include <jni.h>
#ifndef _included_prompt
#define _included_prompt
#ifdef __cplusplus
extern "C" {
#endif
Jniexport jstring jnicall java_prompt_getline (jnienv *env, jobject this, jstring Prompt);
#ifdef __cplusplus
}
#endif
#endif
Jstring is the type that corresponds to string in JNI, but unlike the base type, jstring cannot be used directly as a string of C + +. If you use
cout << prompt << Endl;
The compiler will definitely throw you an error message.
In fact, there are many ways to deal with jstring, here is just one of the simplest ways I think, see the following example,
#include "Prompt.h"
#include <iostream>
Jniexport jstring jnicall java_prompt_getline (jnienv *env, Jobject obj, jstring Prompt)
{
Const char* STR;
str = Env->getstringutfchars (prompt, false);
if (str = = NULL) {
return NULL; /* OutOfMemoryError already thrown */
}
Std::cout << str << Std::endl;
Env->releasestringutfchars (prompt, str);
char* tmpstr = "return string succeeded";
Jstring rtstr = Env->newstringutf (TMPSTR);
return rtstr;
}
In the above example, the prompt as a parameter cannot be used directly by a C + + program, the following conversion is done first
str = Env->getstringutfchars (prompt, false);
Turn the jstring type into a char* type.
When returning, to generate an object of type jstring, you must also use the following command,
Jstring rtstr = Env->newstringutf (TMPSTR);
The Getstringutfchars and Newstringutf used here are the functions that JNI provides for handling string types, and there are other functions that are not listed here.
3. Delivery of array types
Like string, JNI provides the J*array type for an array of Java primitives, such as int[] corresponding to Jintarray. To see an example of passing an int array, the Java program will not write,
Jniexport jint jnicall Java_intarray_sumarray (jnienv *env, jobject obj, Jintarray arr)
{
Jint *carr;
Carr = Env->getintarrayelements (arr, false);
if (Carr = = NULL) {
return 0; /* Exception occurred */
}
Jint sum = 0;
for (int i=0; i<10; i++) {
Sum + = Carr[i];
}
Env->releaseintarrayelements (arr, Carr, 0);
return sum;
}
The getintarrayelements and releaseintarrayelements functions in this example are the functions that JNI provides for working with an array of int. If you try to access the Jintarray type in a arr[i] way, there is no doubt that there will be an error. JNI also provides another pair of functions getintarrayregion and releaseintarrayregion to access the int array, which is not covered, for other primitive types of arrays, the method is similar.
4. Two-D arrays and string arrays
In Jni, both the two-dimensional array and the string array are treated as object arrays because arrays and strings are treated as objects. Still use an example to illustrate, this time is a two-dimensional int array, as the return value.
Jniexport jobjectarray jnicall Java_objectarraytest_initint2darray (jnienv *env, jclass cls, int size)
{
Jobjectarray result;
Jclass intarrcls = Env->findclass ("[I");
result = Env->newobjectarray (size, intarrcls, NULL);
for (int i = 0; i < size; i++) {
Jint tmp[256]; /* Make sure it is large enough! */
Jintarray Iarr = env->newintarray (size);
for (int j = 0; J < size; J + +) {
TMP[J] = i + j;
}
Env->setintarrayregion (Iarr, 0, size, TMP);
Env->setobjectarrayelement (result, I, Iarr);
Env->deletelocalref (Iarr);
}
return result;
}
The third line in the code above,
Jobjectarray result;
Because you want to return a value, you need to create a new Jobjectarray object.
Jclass intarrcls = Env->findclass ("[I");
is to create a jclass reference, because the element of result is a reference to a one-dimensional int array, so intarrcls must be a reference to a one-dimensional int array, how is this guaranteed? Note that the parameter "[i" of Findclass, which is used by JNI to determine the type of the reference, I means the int type, and the [identity is an array. For the other types, there is a corresponding representation method,
Z Boolean
B byte
C Char
S Short
I int
J Long
F float
D Double
String is passed "ljava/lang/string;" , the corresponding String array should be "[ljava/lang/string;".
Or go back to the code,
result = Env->newobjectarray (size, intarrcls, NULL);
The function is to allocate space for result.
Jintarray Iarr = env->newintarray (size);
is to allocate space for one-dimensional int array Iarr.
Env->setintarrayregion (Iarr, 0, size, TMP);
is to assign a value to Iarr.
Env->setobjectarrayelement (result, I, Iarr);
is to assign a value to the first element of result.
With these steps, we create a two-dimensional int array and assign the values so that we can return them as arguments.
If you understand the above, basically most of the tasks can be dealt with. While manipulating array types, especially two-dimensional arrays and string arrays, is cumbersome compared to programming in a single language, since we enjoy the benefits of cross-language programming, we must pay a price.
One thing to add is that the function call method used in this article is for C + +, if you want to use in C, all the env-> are to be replaced by (*env)->, and the following functions need to add a parameter env, specifically, see Jni.h code. There are also some omitted content that can be referenced in the JNI documentation: Java Native Interface 6.0 specification, which can be found in the JDK documentation. If you want to do more in-depth JNI programming, you need to read this document carefully. The next high-level article will also discuss more in-depth topics.
More in-depth topics on JNI programming, including: Accessing Java class Domains and methods in the native method, passing custom classes in Java as parameters and return values, and so on. Knowing this will give you a deeper understanding of JNI programming, a clearer program, and better usability.
1. Define the native method in a generic Java class
In the first two examples, the native method is placed in the Java class of the Main method, and in fact, the native method can be defined entirely in any class. Thus, there is no difference between this class and the other Java classes externally.
2. Accessing the Java class's domains and methods
Although the native method is native, it is a method that, like other methods, can access the private domain and methods of the class. In fact, JNI does do this, and we have a few examples to illustrate,
public class ClassA {
String str_ = "ABCDE";
int number_;
public native void Nativemethod ();
private void Javamethod () {
SYSTEM.OUT.PRINTLN ("Call Java Method succeeded");
}
static {
System.loadlibrary ("ClassA");
}
}
In this example, we define the native method in a Java class that does not have a main method. We will show how to access the domain Str_,number_ and method Javamethod () in Nativemethod (), and the C + + implementation of Nativemethod () is as follows.
Jniexport void Jnicall Java_testclass_classcalldll_nativemethod (jnienv *env, Jobject obj) {
Access field
Jclass cls = Env->getobjectclass (obj);
Jfieldid FID = Env->getfieldid (CLS, "Str_", "ljava/lang/string;");
Jstring jstr = (jstring) Env->getobjectfield (obj, FID);
const char *STR = Env->getstringutfchars (Jstr, false);
if (std::string (str) = = "ABCDE")
Std::cout << "Access field succeeded" << Std::endl;
Jint i = 2468;
FID = Env->getfieldid (CLS, "Number_", "I");
Env->setintfield (obj, FID, i);
Access method
Jmethodid mid = Env->getmethodid (CLS, "Javamethod", "() V");
Env->callvoidmethod (obj, mid);
}
In the above code, the value of STR_ is obtained by the following two lines of code,
Jfieldid FID = Env->getfieldid (CLS, "Str_", "ljava/lang/string;");
Jstring jstr = (jstring) Env->getobjectfield (obj, FID);
The first line of code obtains the ID of the STR_, the type of the STR_ is specified in the call to the Getfieldid function, the second line of code obtains its value through the ID of the str_, and of course we read a jstring type that cannot be directly displayed and needs to be converted to the char* type.
Next we see how to assign a value to the domain of the Java class, look at the following two lines of code,
FID = Env->getfieldid (CLS, "Number_", "I");
Env->setintfield (obj, FID, i);
The first line of code is the same as before, get the ID of the Number_, the second line we assign the value of I to number_ through the Setintfield function, and other similar functions can refer to the JDK's documentation.
The procedure to access Javamethod () is similar to the access domain,
Jmethodid mid = Env->getmethodid (CLS, "Javamethod", "() V");
Env->callvoidmethod (obj, mid);
It is important to emphasize that in getmethodid, we need to specify the type of the Javamethod method, the type of the domain is easy to understand, how the type of the method is defined, in the above example, we use () V,v to indicate that the return value is NULL, () indicates that the parameter is empty. What if a more complex function type is represented? Look at an example,
Long f (int n, String s, int[] arr);
The type symbol for this function is (iljava/lang/string;[ i) j,i represents an int type, ljava/lang/string; represents a String type, [i represents an int array, and J represents a long. These can all be found in the documentation.
3. Using user-defined classes in the native method
JNI not only uses Java's underlying types, but also uses user-defined classes, which makes the most flexibility. In general, there is no big difference between using a custom class and the underlying class using Java (such as String), and the key point is that if you want to use a custom class, you first have to be able to access the constructor of the class, see the following piece of code, we used the custom Java class ClassB in the native method,
Jclass cls = Env->findclass ("LTESTCLASS/CLASSB;");
Jmethodid id = env->getmethodid (CLS, "<init>", "(D) V");
jdouble dd = 0.033;
Jvalue Args[1];
ARGS[0].D = DD;
Jobject obj = env->newobjecta (cls, id, args);
The first thing to do is to create a reference to a custom class that is done with the Findclass function, similar to the previous reference to creating a string object, except that the class name becomes the name of the custom class. The constructor for this class is then obtained through the Getmethodid function, noting that the name of the method here is "<init>", which indicates that this is a constructor.
Jobject obj = env->newobjecta (cls, id, args);
A ClassB object is generated, and args is the parameter of the ClassB constructor, which is a jvalue* type.
Through the three parts described above, the native method has looked exactly like Java's own method, at least the main function is complete, but the implementation of a little trouble. With this in view, the level of JNI programming is also higher. The topic to be discussed below is also an important content, at least without it, our program can only stay in the demonstration phase, not practical value.
4. Exception Handling
Exception handling is an important part of programming in C + + and Java. But in Jni, the trouble comes, the native method is implemented by C + +, if an exception occurs in the native method, how can it be transmitted to Java?
JNI provides a mechanism to implement this functionality. We can throw an exception that Java can receive by the following code.
Jclass Errcls;
Env->exceptiondescribe ();
Env->exceptionclear ();
Errcls = Env->findclass ("java/lang/illegalargumentexception");
Env->thrownew (Errcls, "thrown from C + + code");
If you want to throw other types of exceptions, replace the Findclass parameters. This allows you to receive exceptions thrown in the native method in Java.
"Turn" Android JNI programming Some tricks (grooming)