Summary of the use of JNI

Source: Internet
Author: User


Related concepts


What is JNI?


JNI (Java Native Interface) is a middleware for the C + + language and Java language communication, which is actually a set of interface specifications.





About NKD


The NDK is a collection of tools that are used primarily for cross-compilation.


What is cross-compiling?


Cross-compiling: compiles binary code that can be executed in another platform under one platform .


Note: The NDK can only be run in a Linux environment. Under Windows, you can use Cygwin to run Ndk,cygwin as a Linux environment emulator under Windows.


Related commands:

Clear cache: Ndk-build Clean

Generate a dynamic library: Ndk-build


Click to view "Windows NDK development Environment Construction"


Common terminology for the C + + language


Library functions

compile the packaged functions beforehand .


for code reuse , some commonly used functions (such as input/output) are compiled beforehand, the target code is generated, and the generated target code is packaged into a library file for reuse. The functions in the library file are called function libraries.


In Windows, the object code for the library function is suffixed with. obj, which is suffixed with. O in Linux.


Tip: A single target code cannot be executed directly, and the target code needs to use a connector to concatenate the target code and other library functions together to generate the executable before it runs. Windows (executable file suffix. exe, dynamic library file is. dll), Linux (. So).


Header file

The declaration file for the function.


The header file is a declaration of functions, macros, types, global variables, etc. defined in a library, similar to a warehouse manifest. If you need to use a function from a library in a user program, you only need to include the header file for that library in your program.

Note: The function prototypes for all functions in the library are defined in the header file . the specific implementation of the function is in the library file .

Simply put: The header file is for the compiler, and the library file is for the connector.

When you connect a program to a connector, the corresponding library function is imported into the program, based on the header file that is imported in the user program.

The header file is named suffix. H.


function library

Programmer-Written functions

Dynamic Library

The library functions used in the user program are not connected to the target code of the user program when the user program is compiled, and the corresponding function in the library is called only when the user program executes to the relevant function, so the executable file generated by the dynamic library is relatively small.

Dynamic Library under window:. dll files

The runtime only calls the library function's correlation function

Static Library

When the user program is compiled, the library functions that are used in it are connected to the target code, and the static library is no longer required when the program runs. Using a static library to build the executable file is relatively large.

The library functions used are connected to the target code at compile time, and no further connection to the library function is required at run time.

In Linux:

? Static library naming is generally: lib+ library name +.A.

such as: Libhello.a where Lib indicates that this file is a library file, and Hello is the name of the library. A is the description is static

? Dynamic library naming is generally: lib+ library name +.so. The so description is dynamic.



HelloWorld


1. Create an Android project

2. Declare a native method in the Java layer

Public native String  Callcmethod ();
3. Create a JNI directory and write C code

#include <jni.h>//Method name format is: return value  Java_ Package Name _ Class name _java code method name (jnienv* env,jobject obj) jstring java_com_example_ Hellojni_mainactivity_callcmethod (jnienv* env,  jobject obj) {    return (* (*env)). Newstringutf (env, "This is a string from C");}
4. Writing the Android.mk file

   Local_path: = $ (call My-dir)   include $ (clear_vars)   #模块名称   local_module    : = Hello   #源文件名   LOCAL _src_files: = hello.c   include $ (build_shared_library)
5. Build a dynamic library with NDK compilation

Open the CYGWIN,CD to C source file directory, use the Ndk-build command to generate a dynamic library, the resulting. So file is in the project's Libs directory.


6. Load the dynamic library in the Java layer and call the native method

Load Dynamic library static{system.loadlibrary ("Hello");} Public native String  callcmethod ();p ublic void SayHello () {Toast.maketext (Getapplicationcontext (), Callcmethod () , 0). Show ();}


Java calls a C + + method


1: Define a native method in the Java class


2. Define and write the implementation of the function declaration in C + + in accordance with the JNI interface specification.

Function name format: Return value Java_ Package Name _ Class name _java code method name (jnienv* env,jobject obj)


You can also use the Javah tool to generate a header file:

(1). Enter the classes directory under the project's Bin directory, command:the fully qualified path name of the Javah class


(2). The generated header file method is in the classes directory:
(3). Copy the head file to the project's JNI directory, introduce the header file in the C + + source file, and implement the method


3. Build a dynamic library using NDK compilation.


4. In the Java layer, you can call the native method to indirectly invoke the C + + function. (The library file needs to be loaded first)



C + + method call Java method


The implementation of a C + + call to a Java method is done through reflection .


Related APIs


Steps:
1. Find Class (Findclass)
Find the Java String class
Jclass clazz = (*env)->findclass (env, "java/lang/string");
Parameter description:
Env jnienv*;
The second parameter is the full path name of the class, note that the package name is separated by "/" instead of "." ;

2. Find a method (Getmethodid)
Find the GetBytes (CharSet) method of string
Jmethodid method = (*env)->getmethodid (env, Clazz, "GetBytes", "(ljava/lang/string;) [B");
Parameter description:
Env jnienv*
The second parameter is the method name
The third parameter is the signature of the method (using theJavap–s class full path nameTo see the signatures of all methods in the Class)

3. Call Method (Callxxxmethod)
Call the String.getbytes (CharSet) method to get a byte array
Jbytearray Bytearr = (Jbytearray) (*env)->callobjectmethod (env, str, method, CharSet);
Parameter description:
Env jnienv*
The second argument is the object that called the method
The third parameter is Methodid
The arguments that follow are the arguments that are passed when the method is called

Find static method: Getstaticxxxmethod
Call static method: Callstaticxxxmethod


JNI methods for creating objects:
Jobject Allocobject (env, Clazz)
Jobject NewObject (env, Clazz, Methodid)


View Method signature command: Javap-s ClassName

Using the command line to enter the Bin/classes directory, you can see the signature of all methods of the class using the JAVAP command.


Tip: TheC + + code is ultimately executed in the system process, not in the virtual machine .


Example
Example one: Example of cocos2d-x implementation:
The native method declaration in Java is as follows:
public static native void logout ();
The logout method for calling Java in C + +:
   #if (Cc_target_platform = = cc_platform_android)   #include "jni.h"   #include "jni/jnihelper.h"    // Define the JNI function information structure body    jnimethodinfo minfo;    BOOL Ishave = Jnihelper::getstaticmethodinfo (Minfo, "org.cocos2dx.testcp.TestCpp", "Logout", "() V");    if (ishave) {        cclog ("logout function exist");        Minfo.env->callstaticobjectmethod (Minfo.classid, minfo.methodid);        Cclog ("Java logout function called");    } else{        cclog ("Logout function not exist");    }    #endif

Example two: View the following jstring conversion char*

Correspondence of data types


There is no type in C and C + + that corresponds to string in Java, but you can convert char* to string by using reflection technology.


jstring Conversion char*
Related API Description:Gets the length of the Java array in C
int size = Getarraylenght (env, arr);

Gets the first address of the array, and the third parameter indicates whether the copy is allowed
jint* arr = getintarrayelements (env, arr, 0;)

Free up memory space
Jarr An array of Java passed to C
CArr The first address of the array
Mode The length of the array
Releaseintarrayelements (env, Jarr, CARR, mode);

Implementation of C
char* jstr2cstr (jnienv * env, jstring str) {char* CStr = null;//Find Java String class Jclass clazz = (*env)->findclass (env, "Ja Va/lang/string ");//Find String GetBytes (CharSet) method Jmethodid methods = (*env)->getmethodid (env, Clazz," GetBytes "," ( ljava/lang/string;) [B]; jstring charset = (*env)->newstringutf (env, "UTF-8");//Call String.getbytes (CharSet) The method gets the byte array jbytearray bytearr = (Jbytearray) (*env)->callobjectmethod (env, str, method, CharSet);//Gets the length of the byte array, Request a memory space    int length =  (*env)->getarraylength (Env,bytearr), if (length>0) {CStr = malloc (length+1);// The last position is "\ n"//Copy the data in the byte array to the memory space of the new request char* elements = (*env)->getbytearrayelements (Env,bytearr, 0); strncpy (CStr, elements, length); Cstr[length] = 0;//Indicates the end of the string (in)//releases memory and returns (*ENV)->releasebytearrayelements (env, Bytearr, elements, 0);} return CStr;}
Implementation of C + +
char* jstr2cstr (jnienv * env, jstring jstr) {    char * Rtn = NULL;    Jclass clsstring = Env->findclass ("java/lang/string");    Jstring Strencode = Env->newstringutf ("UTF-8");    Jmethodid mid = Env->getmethodid (clsstring, "GetBytes", "(ljava/lang/string;) [B");    Jbytearray barr= (Jbytearray) Env->callobjectmethod (jstr,mid,strencode);    Jsize alen = Env->getarraylength (Barr);    Jbyte * ba = env->getbytearrayelements (barr,jni_false);    if (Alen > 0)    {        Rtn = (char*) malloc (alen+1);//new char[alen+1];        memcpy (Rtn,ba,alen);        rtn[alen]=0;    }    Env->releasebytearrayelements (barr,ba,0);        return RTN;}


char* Conversion jstring
Implementation of C
(*env)->newstringutf (env, "Hello");

Implementation of C + +
Env->newstringutf ("Hello")


Differences between C and C + + in JNI implementations


JNIEnv


The following code is available in the jni.h file in the Android-ndk-r9d\platforms\android-4\arch-arm\usr\include directory in 153 lines:

#if defined (__cplusplus) //If the C + + language jnienv is defined as _jnienv such a struct
Typedef_jnienv jnienv;
typedef _JAVAVM JAVAVM;
#else
typedef const struct JNINATIVEINTERFACE* jnienv; //Not C + + language defines jninativeinterface such a struct (pointer to _jnienv pointer)
typedef const struct JNIINVOKEINTERFACE* JAVAVM;
#endif


we use Javah generating a header file will generate at least 2 parameters, jnienv* and the Jobject (If the static method is Jclass )

For example: Jniexport jint jnicall java_com_test_test (jnienv *, jobject);
If the signature in the method defined by Java's native has parameters, then the parameter follows Jobject.


in C, we see jnienv we actually get jninativeinterface* (pointer to jnienv pointer) , we have to use **env to get the struct so we can use the method inside the struct.


in C + +, seeing jnienv we are essentially getting the jnienv* (pointer to the jnienv struct) , we can use env-> directly using the method inside the structure.


Differences in method invocations


The difference between C + + method calls:
1. When invoking the JNI function, you do not need to pass ENV
2. (*env), call method simplified to env->

Implementation of C language
Return (*ENV)->newstringutf (env, "Hello from C");
C + + implementation
return Env->newstringutf ("Hello in C");



ANDROID.MK description
ANDROID.MK is a file that writes compilation rules. A subset of the makefile syntax that the cross compiler needs when compiling C/C + + code, Linux.
Common Macro Descriptions:

Bypass JNI direct call C function


The Linux binaries of the Android system are in the System/bin directory, which is already configured in the environment variables of the system, we can execute the files inside the ADB shell directly, so we can also Executes a binary file directly from the Java runtime .

Like what:

PS Get information on all current processes

Ifconfig Getting current Network information


Example code:
Call binaries under Linux in Java code: Process process = Runtime.getruntime (). EXEC ("Binary file Full path name");//Get input stream InputStream is = Process.getinputstream ();//package input stream DataInputStream dis = new DataInputStream (IS);//Read input stream Stringbuild builder = new Stingbuilder (); for (String result; (Result=dis.readline ())!=null;) {builder.append (result); Builder.append ("\ n");} Print Information System.out.println (builder.tostring ());


Chinese garbled problem
If the file written in C code is UTF-8 encoded, you can write the Chinese characters directly.
The low version of the NDK (R4) default encoding set of Iso8859-1, will show garbled, high version of the direct hanging off.

If you want to use the lower version of the NDK, the normal display of Chinese, you can use the following methods to obtain the normal characters:
C:return (*env)-->newstringutf (env, "Hello JNI")
java:string str = new String (string.getbytes ("iso8859-1"), "UTF-8");


Resolving method Name Conflicts
If the native method name in Java is underlined: (add 1 after underline)

Example: Native method for//java layer
Public native StringCall_c_method();

Jniexport jstring Jnicall Java_com_example_hellojni_mainactivity_Call_1c_1method
(JNIENV * env, Jobject obj)
{
Return (*ENV)->newstringutf (env, "This is a string from the C language");
}


Registering JNI functions

The action of registering the JNI function is to establish the association between the Java layer's native method and the native function of the JNI layer.

The association between them is mainly done by a structural body jninativemethod, which is defined as follows:

typedef struct {

const char* name; Java layer's native method name

Const char* Signature; Signature of the Java layer's native method (Javap–s–p ClassName)

void* fnptr; The function pointer of the JNI layer implementation function

} Jninativemethod;

A Jninativemethod array is created at the JNI layer to record the method invocation relationship between the Java layer and the JNI layer.

The Jninativemethod array is then used as the second parameter in the JNIENV function Jint registernativemethod (jclass,jninativemethods, Len) to perform the registration action, Jclass is the class of the Java layer that defines the native method.


Static registration

Implementation steps:

1. Write Java code, define the native method, compile and build the. class file;

2. Use the Javah tool, such as javah–o output packagename.classname, so that it generates a Output.h JNI layer header file;

3. Add the head file to the engineering Jni directory, refer to the header file in C + + code and implement related functions;


The native method in the Java layer is if the implementation function of the JNI layer is found?
When the Java layer calls the XXX method, it looks for the java_packagename_xxx function from the corresponding JNI library, and if it does not, it will error. If found, it will establish an association between the XXX method and the Java_packagename_xxx function, which is actually the function pointer that holds the JNI layer function. When you call the XXX method later, you can use the function pointer directly. This work is done by a virtual machine.


Static registration establishes the association between a Java method and a JNI function based on the function name. There are several drawbacks to this approach :

1. All Java classes that declare the native method need to be compiled, and each generated class file will have to generate a header file with Javah;

The JNI layer functions generated by 2.javah are particularly long and are not easy to write;

3. The initial invocation of the native method is based on the function name to search for the corresponding JNI layer function to establish an association relationship, which will affect the efficiency of operation.


Dynamic registration

The association between the Java layer native method and the JNI layer implementation function is established in the code. Directly let the native method know the function pointer of the JNI layer corresponding function.

The Jni_onload function is called when the dynamic library is loaded, and dynamic registration must implement the Jni_onload function.


Dynamic Registration Sample code:
When the module is loaded, the relationship between the Java layer native method and the native method of the native layer is established Jint jni_onload (javavm* vm, void* reserved) {logi ("loaded mode and call Jni_onload method ");//Create an associative structure array jninativemethod nativemethods[] = {{" Test ",     //java-layer native methods name" () ljava/lang/ String; ", signature of the native method name of the//java layer (void*) test         //native layer method pointer}}; Logi ("Native methods array is created."); /Dynamic registration    //1. Get jnienv*jnienv* env = NULL;    Vm->getenv ((void**) &env, jni_version_1_2);    Logi ("Find class is finish."); /2. Registering the native method//Finding the Java layer defines the native method class    Jclass clazz = Env->findclass ("com/example/jnidynamicreg/mainactivity" );    Jint result = Env->registernatives (Clazz, NativeMethods, 1); Logi ("After register natives result is%d", result);   Return  jni_version_1_2;//Note that it is important to return to jni_version, otherwise it will report an exception}


Summary of the use of JNI

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.