This article describes how to access the properties and methods of any object, of course, access at the native level, and access to the method is generally accessed as a callback for the Java layer. Let's start with the access to the property and the access to the callback function, and then we'll talk about using an efficient and simple caching technique to improve efficiency. Finally, we discuss the performance characteristics of native access Java layer properties and methods.
Access to properties:
The Java language supports two properties, each of which has its own independent property, and all instances share the same static property. JNI provides a Get Set series method to access static and amorphous properties.
Take a look at the following code snippet:
Class Instancefieldaccess { private String s;//non-static property private native void Accessfield ();//local method declaration public static void Main (String args[]) { instancefieldaccess c = new instancefieldaccess (); C.S = "abc";//set s to "abc" C.accessfield ();//local method call, change the value of the string System.out.println ("in Java:"); System.out.println ("C.S = \" "+ C.s +" \ ""); } static { system.loadlibrary ("instancefieldaccess");} }
Let's take a look at how the native method is implemented:
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);//Step 1 printf ("in c:\n"); /* Look for the instance field s in CLS */fid = (*env)->getfieldid (env, CLS, "s", "ljava/lang/string;"); /Step 2 if (fid = = NULL) {return;/* Failed to find the field */}/* Read the instance field S */jstr = (*env)->getobjectfield (env, obj, FID);//step 3, because the string is a reference type 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);}
Program output: in C:
C.S = "ABC"
In Java:
C.S = "123"
Accessing non-static properties requires some fixed step 1.etObjectClass 2.getfieldid,3,getobjectfield, which is a bit similar to the reflection call of the Java layer.
JNI also supports Getintfield, Setfloatfield and so on. You may notice "ljava/lang/string;", which is the JNI attribute descriptor.
The following explains what the descriptor means:
L represents the reference type, you can remember to do language, the property is the reference type of the start with this character, followed by the package name, just "." Replaced by "/"
Z for Boolean, you can remember to do zero for short
The descriptor of the array is [you can remember to do [], i[is int[]
F for float, I for int, etc., this descriptor does not need to be remembered, so to speak, there is a tool that can help us generate this descriptor:
Javap-s-P instancefieldaccess you will get the following output fragment:
...
s ljava/lang/string;
...
Generally we recommend using tools that can help us avoid mistakes.
Let's see how to access the static method: Java Layer
Class Staticfielcdaccess { private static int si;//static I for short. Private native void Accessfield (); public static void Main (String args[]) { staticfieldaccess c = new staticfieldaccess (); Staticfieldaccess.si = +; C.accessfield (); System.out.println ("in Java:"); System.out.println ("staticfieldaccess.si =" + Si); } static { system.loadlibrary ("staticfieldaccess");} }
Native Layer:
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);//Get Class printf ("in c:\n"); /* Look for the static field Si in CLS * /FID = (*env)->getstaticfieldid (env, CLS, "Si", "I");//Get FieldID if (FID = = NULL) { return;/* Field not found */ }/ * Access the static field Si * /si = (*env)->gets Taticintfield (env, CLS, FID);//Get the property value printf ("staticfieldaccess.si =%d\n", si); (*env)->setstaticintfield (env, CLS, FID, 200);//Modify Property value}
The program is entered as follows:
In C:
staticfieldaccess.si = 100
In Java:
STATICFIELDACCESS.SI = 200
The difference between the static and the non-crystalline attributes, the 1.API invocation, the static properties using Getstaticfieldid, the non-static properties using Getfieldid; 2, the API passes the different parameters Getstaticintfield is Jclass
The Getobjectfield is jobject. Compare yourself to the difference.
Access methods are also divided into two types, static and non-static methods:
Examples of accessing instance methods: Java Layer
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");} }
Native Layer
Jniexport void Jnicall Java_instancemethodcall_nativemethod (jnienv *env, Jobject obj) { Jclass cls = (*env) Getobjectclass (env, obj);//Step 1 jmethodid mid = (*env)->getmethodid (env, CLS, "Callback", "() V");//Step 2 if ( Mid = = NULL) { return;/* Method not Found * /} printf ("in c\n"); (*env)->callvoidmethod (env, obj, mid);//Step 3}
Similarly, 3 steps
Output Result:
In C
In Java
If Getmethodid returns null then Nosuchmethoderror will be thrown, Callvoidmethod incoming jobject, JNI has a family of functions:
Call<type>method Type can make object Void, int, and so on
You may have noticed that the "() V" is a method descriptor that you can generate using a tool:
Javap-s-P Instancemethodcall you will get the following output:
...
Private callback () V
public static main ([ljava/lang/string;) V
Private native Nativemethod () V
...
Simply explain the meaning of this descriptor
Native private string GetLine (string); The descriptor is "(ljava/lang/string;) ljava/lang/string;", in parentheses is the parameter, followed by the return value type
public static void Main (string[] args); Descriptor is "([ljava/lang/string;) V"
Access static method: Here only the code is posted, do not explain, with access to today too attribute one truth:
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");} }
Jniexport void Jnicall Java_staticmethodcall_nativemethod (jnienv *env, Jobject obj) { Jclass cls = (*env) Getobjectclass (env, obj); Jmethodid mid = (*env)->getstaticmethodid (env, CLS, "Callback", "() V");//Get Methodid if (mid = = NULL) { Return /* Method not found * /} printf ("in c\n"); (*env)->callstaticvoidmethod (env, CLS, mid);//Incoming Jclass}
The output is as follows:
In C
In Java
Here's a more interesting way to access the parent class, and you'll see what C + + looks like:
JNI provides a family of API Callnonvirtual<type>method to access the parent class's methods, and for subclasses, the subclass inherits the parent class and inherits the parent class's methods, but for JNI it is necessary to distinguish between what is a subclass of replication override, which are not replicated. In C + +, there is the concept of virtual function, can be compared. Other references access static and non-static methods. For a method, there is a difference between a parent class and a subclass, and for a property it does not seem to use the same set of APIs.
Callnonvirtualvoidmethod can be used to access the constructor of the parent class. Consider the following native code, which calls the constructor method to return a string
Jstring mynewstring (jnienv *env, Jchar *chars, Jint len) {Jclass stringclass; Jmethodid CID; Jchararray Elemarr; jstring result; Stringclass = (*env)->findclass (env, "java/lang/string");//Find String class if (Stringclass = = null) {return null; /* Exception thrown */}/* Get The method ID for the String (char[]) constructor */cid = (*env)->getmethodid ( Env, Stringclass, "<init>", "([C) V");//Gets the Methodid if (CID = = null) of the construction method {return null;/* Exception thrown */}/* Create a char[] that holds the string characters */Elemarr = (*env)->newchararray (env, Len);//new a CH Ar[] As a temporary variable if (Elemarr = = NULL) {return null;/* Exception thrown */} (*ENV)->setchararrayregion (env , Elemarr, 0, Len, chars);//Assign a temporary variable, copy the incoming char* to the new Elemarr/* Construct a Java.lang.String object */result = (*env)- >newobject (env, Stringclass, CID, Elemarr);//Call construction method/* Free local References */(*ENV)->deletelocalref (env, El EmARR); (*env)->deletelocalref (env, stringclass); return result;}
This example is more complicated and worth explaining in detail: Getmethodid is actually getting the string (char[] chars). Constructor method, as a constructor, the return value is void, because the Java layer constructor method does not return a value.
Deletelocalref we'll introduce you in the next section. We used to have a similar method of generating Strings NewString series, which is also a way to generate strings, but the former is more convenient and efficient, string is also very common, so JNI designed a set of APIs to support the operation of strings.
The following code fragment
result = (*env)->newobject (env, Stringclass, CID, Elemarr);
It can be another form: use Allocobject to create an "uninitialized" object, which is allocated memory, but not initialized. You can only call a construction method on this block of memory, and only once. Not called, or multiple calls can result in an error.
result = (*env)->allocobject (env, Stringclass), if (result) { (*env)->callnonvirtualvoidmethod (env, result, Stringclass, CID, Elemarr);//Direct trigger construction method/ * We need to check for possible exceptions * * if ((*env)->EXCEPTIONCHEC K (env)) {//followed by (*env)->deletelocalref (env, result);//release reference result = NULL;} }
This form of use can easily lead to errors and usage is somewhat complex, so we'd better use the NewString series to manipulate strings.
The ID of the cache method and property
We need to look up the symbol table for the ID of both the method and the property, which is quite time consuming and slightly expensive. Therefore, the cache reuse will be more efficient when the search is complete. There are two methods of caching, 1 using cache, 2 through static code block caching, and the following describes the two methods:
1, cached when used
Jniexport void Jnicall Java_instancefieldaccess_accessfield (jnienv *env, Jobject obj) { static jfieldid fid_s = NULL; /* Cached field ID for S, here is the key, using static only first call initialization * /Jclass CLS = (*env)->getobjectclass (env, obj); Jstring Jstr; const char *STR; if (fid_s = = NULL) {//First call 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);//multiplexing cache 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);//Reuse Cache}
This method leads to a competition problem in multi-threading, and the result is repeated initialization, but repeated initialization does not cause the program to run incorrectly, no harm.
2. Initialize in a static code block, Java layer Code:
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 (); }}
Native Layer code:
Jmethodid mid_instancemethodcall_callback;//global variable jniexport void Jnicall java_instancemethodcall_initids (JNIEnv *env, Jclass cls) { Mid_instancemethodcall_callback = (*env)->getmethodid (env, CLS, "Callback", "() V");}
It is obvious that this method uses a global variable, and the next time the method ID is used, it is directly:
Jniexport void Jnicalljava_instancemethodcall_nativemethod (jnienv *env, Jobject obj) { printf ("in c\n"); (*env)->callvoidmethod (env, obj, mid_instancemethodcall_callback);}
Comparison of the two caching methods:
Run-time caching policies require one or more check and init.
Method and Field IDs will remain in effect until the class is unloaded. If you use a run-time caching policy, you must ensure that class is not unloaded and reloaded, in other words, your class cannot be unloaded and reloaded until your native method relies on the cached ID, and the next chapter will go to how to ensure that your class is not unloaded. If you cache using static blocks of code, then the class is unloaded and reloaded, and the ID is recalculated. It is therefore recommended to use static blocks of code.
The performance of properties and methods is manipulated in a jni manner:
Native method Access Java method native method Access Native method Java method Access Java method, which of the three ways, which is the most effective????
This problem relies on the way that virtual machines implement JNI, where we discuss only the inherent overhead and only the general situation.
In general, java/native calls are less efficient than Java/java calls because: the native method is likely to follow a new invocation rule, and the result is that the virtual machine must make the appropriate conversion for this change, such as constructing some new data structure settings stack and so on, inline Java /native methods are more complex than inline java/java methods. After a cursory test, the java/native call is 2-3 times slower than the Java/java, and the Native/java call is slower than the java/native call. In practice, the native callback Java method is rare, the virtual machine will not often optimize the performance of callback Java method, the document says, when writing this document, native callback Java method, than Java call Java Method 10 times times slower. There is a noticeable overhead, so if it's not particularly necessary, we'd better not let the native method call the Java layer method.
However, access to the Java layer of properties is not so much different, can be ignored, the reason is not translated, remember the conclusion can be.
Previous:JNI Official document Translation 3-basic data type string array
Next: JNI Official document Translation 5-local and global references
JNI Official Document Translation 4-access to properties and methods