Go JNIEnv parsing

Source: Internet
Author: User

1. About JNIEnv and JAVAVM

JNIEnv is a thread-dependent variable, and the jnienv of different threads are independent of each other. JAVAVM is the representation of the virtual machine in the JNI layer, where there is only one JAVAVM in a virtual machine process, so that this JAVAVM can be used by all threads of the process. When a background thread needs to invoke JNI native, it is especially important to use global variables in the native library to save JAVAVM, which allows the background thread to get jnienv through JAVAVM.

Jnienv* and javavm* are frequently used in native programs. While C and C + + code use the two pointers jnienv* and javavm* are different, most of the online code uses C + +, basically can not find a detailed description of C and C + + on this issue.

In C:

Use jnienv* env to do this (*env), Method name (env, parameter list)

Use the javavm* vm like this (*VM), method name (VM, parameter list)

In C + +:

Use jnienv* env to do this env-> method name (parameter list)

Use javavm* VM to vm-> method name (parameter list)

The difference between the above is that Env and VM must be addressed indirectly in C (the resulting content is still a pointer), and the Env or VM is passed in as the first parameter when calling the method. C + + invokes its members directly with Env and VM pointers. Does the Env in C (*env) and C + + have the same data type? Does the VM in C (*VM) and C + + have the same data type?

To verify the above guesses, we can view the definitions of jnienv and JAVAVM. They are located in the header file jni.h. I developed JNI using the Android-5 platform, which is part of the code for $NDK \platforms\android-5\arch-arm\usr\include\jni.h

struct _jnienv;    struct _JAVAVM;                                 #if defined (__cplusplus) typedef _jnienv JNIENV;                                 C + + uses this type of typedef _JAVAVM JAVAVM;        C + + uses this type #else typedef const struct JNINATIVEINTERFACE* jnienv;        C Use this type typedef const struct JNIINVOKEINTERFACE* JAVAVM; C Use this type #endif struct Jninativeinterface {/**** omitted code ****/jmethodid (*getmethodid) (jnienv*, J        class, const char*, const char*);        /**** omitted code ****/jobject (*getstaticobjectfield) (jnienv*, Jclass, Jfieldid);    /**** omitted code ****/};      struct _JNIENV {const struct jninativeinterface* functions;       #if defined (__cplusplus)/**** omitted code ****/jmethodid getmethodid (jclass clazz, const char* name, const char* SIG)      {return Functions->getmethodid (this, clazz, name, SIG);} /**** omitted code ****/jobject Getstaticobjectfield (Jclass clazz, Jfieldid fieldid) {return functions->getstaticobjectfield (This, clazz, FieldID);    }/**** omitted code ****/#endif/*__cplusplus*/};      struct Jniinvokeinterface {/**** omitted code ****/jint (*getenv) (javavm*, void**, Jint);  Jint (*attachcurrentthreadasdaemon) (javavm*, jnienv**, void*);    };      struct _JAVAVM {const struct jniinvokeinterface* functions; #if defined (__cplusplus)/**** omitted code ****/jint GETENV (void** env, jint version) {return functions->gete NV (This, env, version); } jint Attachcurrentthreadasdaemon (jnienv** p_env, void* Thr_args) {return FUNCTIONS->ATTACHCURRENTTHREADASD Aemon (This, p_env, Thr_args); } #endif/*__cplusplus*/};

  

If we use C code, the macro __cplusplus is not defined, then from the topmost macro # if defined (__cplusplus) can infer

JNIENV represents a type const struct jninativeinterface*

JAVAVM represents a type const struct jniinvokeinterface*

Then jnienv* env is actually equivalent to declaring the const struct jninativeinterface** env

javavm* VMs are actually equivalent to declaring a const struct Jniinvokeinterface * * VM

Therefore, to invoke a function pointer within the JNINATIVEINTERFACE structure, you must first address env indirectly.

The type of (*ENV) is the const struct jninativeinterface* (pointer to the jninativeinterface struct), which can be used to invoke the member function pointer of the struct, (*env), Getmethodid (env, Jclass, const char*, const char*). You can analyze the javavm* VM in the same vein.

--------------------------------------------------------------------------------------------------------------- -------------------------------

If we encode in C + +, macro __cplusplus is defined, then from the top of the macro # if defined (__cplusplus) can infer

JNIEnv represents type struct _JNIENV

JAVAVM represents type struct _JAVAVM

Then jnienv* env is actually equivalent to declaring struct _jnienv* env

javavm* VMs are actually equivalent to declaring a struct _javavm* vm

To invoke a function pointer within the _JNIENV structure this directly uses env without the need for indirection, env-> getmethodid (jclass, const char*, const char*). You can analyze the javavm* VM in the same vein.

Now you can answer the speculation, C (*env) type is const struct jninativeinterface*,c++ in the env type is a struct _jnienv*, so their data type is not the same (although all pointers, But pointing to different struct types).

Let's look at the struct _jnienv (the type represented by C + + jnienv), which has a member const struct jninativeinterface* functions, and then a closer look at the functions defined in _jnienv. When a function defined within _JNIENV is called, the function pointer inside the jninativeinterface is actually called through the functions pointer, so _ The member method of the jnienv is the wrapper of the Jninativeinterface member function pointer, which in the final analysis uses the jninativeinterface struct in both C and C + +. The first parameter of the function pointer called Jninativeinterface is this, which in C + + represents a pointer to the current context object whose type is struct _jnienv* (that is, jnienv*). The same can be analyzed _JAVAVM.

2. Registering and Unregistering native functions

C and C + + register the native function in roughly the same way, given the specific code below.

[CPP]View Plaincopy
  1. /* The Jninativemethod array is defined in C and C + + * *
  2. Static Jninativemethod gmethods[] = {
  3. {
  4. "Jobjectprocess",
  5. "(lcom/example/hellojni/hellojni$student; Ljava/lang/integer;) V ",
  6. (void*) jobjectprocess
  7. }
  8. / * The omitted code * /
  9. };
  10. Jint jni_onload (javavm* vm,void* reserved)
  11. {
  12. jnienv* env = NULL;
  13. Jint Result=-1;
  14. if (*VM)->getenv (VM, (void**) &env, jni_version_1_4)! = JNI_OK)
  15. return result;
  16. Jclass hellojniclazz= (*env)->findclass (env,"Com/example/hellojni/hellojni");
  17. / * C * /
  18. Jint r= (*env)->registernatives (env, Hellojniclazz, Gmethods, sizeof (gmethods)/ sizeof (Jninativemethod));
  19. / * C + + *
  20. R=androidruntime::registernativemethods (env,"Com/example/hellojni/hellojni", Gmethods,nelem (GMethods));
[CPP]View Plaincopy
  1. /* or env->registernatives (hellojniclazz, Gmethods, sizeof (gmethods)/sizeof (Jninativemethod)); */
  2. if (0 = = r)
  3. //Registered native function succeeded
  4. Else
  5. //Register native function failed
  6. return jni_version_1_4;
  7. }
  8. void Jni_onunload (javavm* vm,void* reserved)
  9. {
  10. jnienv* env = NULL;
  11. if (*VM)->getenv (VM, (void**) &env, jni_version_1_4)! = JNI_OK)
  12. return;
  13. Jclass hellojniclazz= (*env)->findclass (env,"Com/example/hellojni/hellojni");
  14. / * C * /
  15. Jint r= (*env)->unregisternatives (Env,hellojniclazz);
  16. / * C + + *
  17. Jint r= env->unregisternatives (hellojniclazz)
  18. if (r = = 0)
  19. //Logoff native function succeeded
  20. Else
  21. //Logoff native function failed
  22. }

Both C and C + + can be registered through JNIENV's registernatives function, and C + + also provides androidruntime::registernativemethods, The Registernativemethods method of the Androidruntime class can also be registered.

3. Output debug information to Logcat in native

Add the header of the C + + compilation unit

#include <android/log.h>

#define LOG_TAG "Customizing a String"

Log.h declares the function int __android_log_print (int prio, const char *tag, const char *FMT, ...) We are using this function to output information to the Logcat.

After adding the header file, you must also give the linker the library file liblog.so that contains the __android_log_print function, adding a line to the Android.mk file

Local_ldlibs: =-llog

In the native function, you can use the following statement to output the

__android_log_print (Android_log_debug,log_tag, "name=%s,age=%d", "Lu Binhui", 28);

The first parameter, Android_log_debug, is an enumeration constant, which is defined in Log.h.

[CPP]View Plaincopy
  1. typedef ENUM Android_logpriority
  2. {
  3. Android_log_unknown = 0,
  4. Android_log_default, /* only for setminpriority () */
  5. Android_log_verbose,
  6. Android_log_debug,
  7. Android_log_info,
  8. Android_log_warn,
  9. Android_log_error,
  10. Android_log_fatal,
  11. Android_log_silent, /* only for setminpriority (); must is last */
  12. } android_logpriority;

We can choose different enumeration constants based on the different categories of debug information.

4. About Jclass

Jclass represents the Java.lang.Class in Java. Let's look at the definition of jclass and give a partial code of $NDK\PLATFORMS\ANDROID-5\ARCH-ARM\USR\INCLUDE\JNI.H

[CPP]View Plaincopy
  1. #ifdef __cplusplus
  2. /*reference types, in c++*/
  3. Class _jobject {};
  4. Class _jclass: Public _jobject {}; /*_jclass inheritance _jobject*/
  5. typedef _jclass* Jclass;
  6. #else
  7. /*reference types, in c.*/
  8. typedef VOID* Jobject;
  9. typedef jobject Jclass;
  10. #endif

In C, Jclass represents type void*, which represents type _jclass* in C + +. So Jclass is a pointer and we can output the value of the Jclass variable in log.

__android_log_print (Android_log_debug, "output in native function", "Address =%p", jclass variable);

When multiple native functions need to use the Jclass variable of the same Java class, it is not possible to define the Jclass type global variable and only assign it an initial value once and then use this jclass variable in the native function call multiple times in Java. It is not possible to attempt to conserve the cost of obtaining jclass variables in this way.

Every time the Java call native must regain Jclass, the jclass of the last call to native is invalid when the next call to native is made. Here is the C code

[CPP]View Plaincopy
  1. Static Jclass Studentclazz; //global variable
  2. Jint jni_onload (javavm* vm,void* reserved)
  3. {
  4. jnienv* env = NULL;
  5. Jint Result=-1;
  6. if (*VM)->getenv (VM, (void**) &env, jni_version_1_4)! = JNI_OK)
  7. return result;
  8. studentclazz= (*env)->findclass (env,"com/example/hellojni/hellojni$student"); //Initialize
  9. return jni_version_1_4;
  10. }
  11. Jniexport void Jnicall jobjectprocess (jnienv *env, jobject instance,jobject student,jobject flag)
  12. {
  13. /*studentclazz= (*env)->findclass (env, "com/example/hellojni/hellojni$student"); * *
  14. __android_log_print (android_log_debug,"output in Jobjectprocess","studentclazz=%p", Studentclazz);
  15. Namefieldid= (*env)->getfieldid (Env,studentclazz,"name","ljava/lang/string;");
  16. Jstring name= (jstring) ((*env)->getobjectfield (Env,student,namefieldid));
  17. }

Here's the code for the activity.

[Java]View Plaincopy
  1. Static
  2. {
  3. System.loadlibrary ("Hello-jni");
  4. }
  5. Public native void jobjectprocess (Student student,integer flag);
  6. Public static class student{/* omitted code */}
  7. protected void Onresume ()
  8. {
  9. Jobjectprocess (new Student (),new Integer (20));
  10. Super.onresume ();
  11. }

The above C code initializes the Studentclazz in the Jni_onload function and does not assign a value to Studentclazz within the jobjectprocess function. At this point the running program will be faulted and output the following information in Logcat:

debug/output in Jobjectprocess (8494): studentclazz=0x44c0a8f0

WARN/DALVIKVM (8286): Jni warning:0x44c0a8f0 is not a valid JNI reference

WARN/DALVIKVM (8286): in Lcom/example/hellojni/hellojni;. Jobjectprocess (lcom/example/hellojni/hellojni$student; Ljava/lang/integer;) V (Getfieldid)

Tip Studentclazz The address pointed to (0X44C0A8F0) is not a valid JNI reference. If you remove the first line of comments from the Jobjectprocess function, the Studentclazz assignment is performed normally.

In fact, regardless of which native function gets the Studentclazz value is the same, but each time the native call or must perform a findclass to Studentclazz assignment again.

5.native char* and Java strings convert to each other

First, make sure that the C + + source file character encoding is UTF-8 consistent with the Java class file character encoding. If C + + source contains Chinese, the compiled so Chinese string is also saved as UTF-8 encoding, such a program does not produce garbled.

JNI provides jstring to reference the Java string type variable, which must be used to jstring if the native function needs to return a string or accept a String type argument. Instead, C + + uses char* to refer to the starting address of the string, and when the native function is connected to jstring, the string to be converted to char* is processed. When we finish processing the string that char* points to, it is also converted to jstring to return to the Java code. The following is the method of conversion (c code below).

Jstring convert to char* using jnienv const char* getstringutfchars (jnienv*, jstring, jboolean*)

jnienv env=//incoming parameters; jstring name=//incoming parameters;

const char *namestr= (*ENV)->getstringutfchars (env,name,null);

After you call Getstringutfchars, you must call JNIEnv's void Releasestringutfchars (jnienv*, jstring, const char*) to release the newly created string.

(*env), Releasestringutfchars (Env,name, NAMESTR);

Char* converted to jstring using jnienv jstring newstringutf (jnienv*, const char*);

Jstring newargname= (*env)->newstringutf (env, NAMESTR);

You must call JNIEnv's void Deletelocalref (jnienv*, jobject) After you call Newstringutf, and release the newly created jstring.

(*env), Deletelocalref (env, newargname);

Go JNIEnv parsing

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.