Android platform JNI call

Source: Internet
Author: User

I recently contacted the Android platform JNI call and found that the network information has not provided a more detailed introduction from the principle to the specific implementation. Therefore, I have referenced some materials and added some experiences based on the project development:

Origin of JNIJNI is the abbreviation of Java Native Interface. It allows Java code to interact with code written in other languages. JNI is a local programming interface. It enables the Java code running inside the Java Virtual Machine (VM) to interoperate with applications and libraries written in other programming languages (such as C, C ++, and assembly languages.
Reason for using JNI 1. the standard Java library for platform relevance does not support some platform features. You can write code in other languages to make your software support these platform features, such) the mechanism is not supported (message queue, shared memory, semaphore, etc ).
2. To improve efficiency, you may need to write some algorithms in low-level languages to improve program performance. For example, Java database access and socket communication are less efficient.
3. the Java level of Android is the shell framework. Most Android system controls are on the native layer (C/C ++) to solve problems related to the native layer.
4. I want to use the library files that have been written in C/C ++ on the Java layer of the Android platform. 5. Use C to develop the application logic that requires confidentiality. After all, Java packages can be decompiled.

 

Hierarchical relationship diagram of JNI in Android: Android uses Java to call C/C ++ 1. Compile a Java class with native declarations1. Define Java methods with native keywords and cannot be implemented in Java; Package Sam. Test. jnitest; public class jnitest extends activity {/** called when the activity is first created. */@ override public void oncreate (bundle savedinstancestate ){ Super. Oncreate (savedinstancestate);} public native void test (); Public native string stringcat (string str1, string str2); Public native int reduce (int x, int y);} 2. load library files implemented by C/C ++ in Java code. For example, add: static {system. loadlibrary ("JNI-test");} load a C/C ++ library named JNI-test without a suffix, in order to be compatible between different operating systems (Exaggerated Platform), because the database Suffix of each platform is different, such as libjni-test.so or libjni-test.dll; 3. call this native method in Java (same as calling other class methods ). 2. compile it into a class fileFirst, make sure that you have downloaded and installed JDK, and configure the system environment variable of JDK 1. one method is to compile through javac. You can use javac JniTest in the command line. java Compilation: E: \ workspace \ JniTest \ src> javac-classpath E: \ Working \ Android \ SDK \ android-sdk-windows \ platforms \ android-6/android. jar sam/test/jnitest. java is finally stored in jnitest. java directory to generate jnitest. in addition, you can use the-classpath option to add the jar package dependent on the class file. In this example, JniTest inherits the Activity class, And the javac compilation process must be added to android. jar package. 2. Another method is to compile with Eclipse. You can find the compiled JniTest. class file directly in the bin directory of the Eclipse project. 3. Use the javah command to generate a header file with the extension H.1. process the class file generated by javac: E: \ workspace \ JniTest \ src> javah sam. test. jnitest. jniTest 2. process the class file E: \ workspace \ JniTest \ src> javah-classpath compiled by Eclipse .. /bin sam. test. jnitest. jniTest 3. Note that the class should contain the package name. In the above example, the class name is jnitest and the package name is sam. Test. jnitest. 4. classes in all packages must be included in the path folder. Otherwise, a class error cannot be found. In the above example, the working directory of javac compilation is workspace \ jnitest \ SRC, which already contains the class Sam. test. jnitest (the hierarchy in Java is similar to the hierarchy of folders, class Sam. test. the directory corresponding to jnitest is Sam/test/jnitest) 5. the classpath parameter specifies the first-level folder before the package name. For eclipse compilation, specify bin as the first-level directory of the package name and generate the sam_test_jnitest_jnitest.h file, which defines native functions mapped from Java to C/C ++: jniexport void jnicall initialize (parameters *, jobject); jniexport jstring jnicall initialize (parameters *, jobject, jstring, jstring); jniexport jnicall initialize (jnienv *, jobject, jint, jint); the function parameters are as follows: jnienv *: pointer of the JNI environment, virtual A handle of the current thread in the machine, including the ing information and other operation information (a pointer to the JNI environment. this pointer is a handle to the current thread in the Java Virtual Machine, and contains mapping and other hosuekeeping information ). The VM is a multi-threaded execution environment, and each thread has different IDs when calling the JNI function. Jobject: function reference that calls the local code. (A reference to the method that called this native code), you can use this parameter and jnienv * to obtain the corresponding Java class for calling this function. After jnienv * jobject: it corresponds to the specific input parameter when Java code calls the native function. The generated header file shows some JNI syntax rules. For example, the registered Native function always starts with Java _, followed by the package name_class name, and finally the function name. For detailed rule content, see the official documentation: http://download.oracle.com/docs/cd/E17476_01/javase/1.4.2/docs/guide/jni/spec/jniTOC.html 4. Use C/C ++ to implement local methods1. generated according to step 3. c/C ++ function declaration in the H file. Add the specific method to hello. c: JNIEXPORT jstring JNICALL encode (JNIEnv * env, jobject thiz, jstring jstrSrc, jstring jstrDes) {char buffer [512]; android_log_print (ANDROID_LOG_INFO, "JniTest ", "stringcat Begin ...... "); const char * pSrc; = (* env)-> GetStringUTFChars (env, jstrSrc, NULL); if (pSrc = NULL) return NULL; const char * pDes = (* env)-> GetStringUTFChars (env, jstrDes, NULL); if (pDes = NULL) return NULL; strcpy (buffer, pSrc ); strcat (buffer, pDes); (* env)-> ReleaseStringUTFChars (env, jstrSrc, pSrc); (* env)-> ReleaseStringUTFChars (env, jstrDes, pDes ); return (* env)-> NewStringUTF (env, buffer);} JNIEXPORT jint JNICALL encode (JNIEnv * env, jobject, jint I, jint j) {return I-j ;} if the file is written in C ++, we can implement this part in a more concise way: extern "C" JNIEXPORT JstringJNICALL Java_sam_test_jnitest_JniTest_stringcat (JNIEnv * env, jobject thiz, jstring jstrSrc, jstring jstrDes ){... Const char * pSrc; = env-> GetStringUTFChars (jstrSrc, NULL );... return env-> NewStringUTF (buffer);} 2. add the entry function JNI_OnLoad () when the Android VM (Virtual Machine) runs to the System. when loadLibrary () is used, the JNI_OnLoad () function in component C is executed first. It has two purposes: • tell the VM that the C component uses the JNI version. If your *. so file does not provide the JNI_OnLoad () function, VM will use the oldest JNI 1.1 version by default *. so file. Because the new version of JNI has made a lot of extensions, if you need to use the new version of JNI features, such as JNI 1.4's java. nio. ByteBuffer, you must use the JNI_OnLoad () function to tell the VM. • Since the VM runs to System. when the loadLibrary () function is used, JNI_OnLoad () is called immediately. Therefore, the C component developer can set the initial value (Initialization) 1) in the C component in JNI_OnLoad ). create a ing table between jni and JniTest classes static JNINativeMethod gJniTestMethods [] = {/* name, signature, funcPtr */{"test", "() V", (void *) java_sam_test_jnitest_JniTest_test },{" Stringcat"," (Ljava/lang/String;) Ljava/lang/String; ", (void *) Java_sam_test_jnitest_JniTest_stringcat}, {" reduce ", "(II) I", (void *) Java_sam_test_jnitest_JniTest_reduce},}; Andoird uses a different traditional Java JNI method to define its native function. An important difference is that Andorid uses a ing table array of Java and C functions, and describes the parameters and return values of the functions. The type of this array is JNINativeMethod, which is defined as follows: typedef struct {const char * name;/* name of the function in Java */const char * signature; /* describes the function parameters and return values */void * fnPtr;/* function pointer, pointing to the C function */} JNINativeMethod; the first parameter corresponds to the function name in java, the second parameter is hard to understand, for example, "() V" "(II) V" "(Ljava/lang/String ;) in fact, these characters correspond to the function parameter types one by one. The character in "()" represents a parameter, and the subsequent character represents the return value. For example, "() V" indicates void Func (); "(II) V" indicates void Func (int, int ); the correspondence between each character is as follows: Java C type V void Z jboolean boolean I jint int J jlong long D jdouble double F jfloat float B jbyte byte C jchar char S jshort short the array starts, use two characters to represent [I jintArray int [] [F jfloatArray float [] [B jbyteArray byte [] [C jcharArray char [] [S jshortArray short [] [D jdoubleArray double [] [J jlongArray long [] [Z jbooleanArray Boolean [] is of the basic type. If the Java function parameter is a class, it starts with "L" and ends with ";", with the package and class names separated by "/" in the middle. The corresponding C function name parameter is jobject. one exception is the String class, whose corresponding classes are jstring Ljava/lang/String; String jstring Ljava/net/Socket; Socket jobject if the JAVA function is located in an embedded class, $ is used as the separator between class names. For example, "(Ljava/lang/String; Landroid/OS/FileUtils $ FileStatus;) Z" 2 ). register for different native functions in the class • Custom registerNativeMethods functions: static int functions (JNIEnv * env, const char * className, JNINativeMethod * gMethods, int numMethods) {jclass clazz; clazz = (* env)-> FindClass (env, className); if (clazz = NULL) return 0; if (* env)-> RegisterNatives (env, clazz, gMethods, numMethods) <0) return 0; return 1 ;} This method is generally used for the code Compiled by NDK (Native Develepment kit, see 5.1). The registerNativeMethods interface in NDK is not yet open, so you need to customize the implementation, this part of code is consistent with the code of registerNativeMethods In the Android source code. For details, refer .. \ dalvik \ libnativehelper \ JNIHelp. implementation of jniRegisterNativeMethods in c. • If the source code is compiled in the Android source code, you can include AndroidRuntime. h and then directly call the AndroidRuntime: registerNativeMethods function for registration. • RegisterNatives function: When a Java program at the application level calls a local function, the local function in the library file is searched through the Virtual Machine. If a function needs to be called frequently and it takes a lot of unnecessary time to search for each call, developers can register the local function with the virtual machine, to find functions more efficiently. 3 ). call JNI_OnLoad and return the JNI version Virtual Machine jint JNI_OnLoad (JavaVM * vm, void * reserved) {JNIEnv * env = NULL; _ android_log_print (ANDROID_LOG_INFO, "JniTest ", "JNI_OnLoad ...... "); if (* vm)-> GetEnv (vm, (void **) & env, JNI_VERSION_1_4 )! = JNI_ OK) return JNI_FALSE; if (! Optional (env, "sam/test/jnitest/JniTest", gJniTestMethods, sizeof (gJniTestMethods)/sizeof (gJniTestMethods [0]) return JNI_FALSE; return expected;} 4 ). add JNI_Unload as needed. JNI_Unload corresponds to JNI_Load. When the Virtual Machine releases the c component, JNI_Unload is called to clean up the problem. 5. Compile the C/C ++ source file into a library file1. Compile with ndk1 ). ndk introduction • ndk stands for Native Development Kit. It provides a series of tools to help developers quickly develop C (or C ++) dynamic libraries, the so library file and Java application can be automatically packaged into an APK. • The ndk integrates the cross compiler and provides MK files to isolate differences such as CPU, platform, and Abi, developers can create so simply by modifying the MK file (indicating "which files need to be compiled" and "Compilation feature requirements. • Ndk provides a stable and functional API header file statement that supports very limited functions, including: C standard library (libc) and Standard Math Library (libm), compression library (libz), log Library (liblog ). 2) configure the environment windows: a).download android-ndk-r4-windows.zip. B) install cygwin 1.7 or later, a simulated Linux environment, and the required components. After the installation is successful, configure the environment variable: Modify home \ <your username> \ in the Windows installation directory \. bash_profile file, add environment variable ndk =/cygdrive/<your drive letter>/<Android ndk directory> For example: ndk =/cygdrive/D/Android/android-ndk-r4-windows

The name "ndk" in the export ndk can be used at will, because it is often used later. We recommend that you do not use it too long. Restart cygwin and enter CD $ ndk to enter the corresponding directory. c) Ensure that JDK 5 and later are installed. Linux: You can also install VMware in a Windows environment to simulate a Linux development environment with a graphical interface. The environment configuration is the same as the following: a). Download The ndkdevelopment package (for example, android-ndk-1.6_r1-linux-x86.zip ). B) run it in the terminal: gedit ~ /. Bashrc in the Open configuration file to add the environment variable for the current user: ndk = <Android ndk directory> For example: ndk =/home/android_ndk_1.6/android-ndk-1.6_r1
Export NDK c). Make sure that JDK 5 and later are installed. 3 ). edit the compilation script to create a jni directory under the android project directory, copy the C/C ++ source file to this directory, generate and edit Android. mk: LOCAL_PATH: = $ (call my-dir) include $ (CLEAR_VARS) LOCAL_MODULE: = jni-test LOCAL_SRC_FILES: = hell. c LOCAL_LDLIBS: =-llog include $ (BUILD_SHARED_LIBRARY) specifies the module name (jni-test), the file to be compiled (hell. c), the compilation method (Dynamic Link Library), and the library files that the program depends on (for example,-llog uses android to print output information ). 4 ). compile (cygwin for windows) cd to the project directory, run $ NDK/ndk-build, NDK compile and generate library files (libjni-hello.so ), you can go to the project directory libs/armeabi to verify the existence of the library file. 5) run the .apk program directly behind the successful compilation on the simulator host device. The. APK file compiled by ndkhas been packaged. so file. If you do not want to automatically package the file, you can download the file in the libs/armeabi directory of the android project. the so library is deleted and compiled to generate an apk with no package files, and then manually start adb. the so library file is pushed to the system/libs directory of the simulator or device. However, the system/lib directory of the simulator or device is read-only by default. You must first change its attribute to writable before using adb to push the library file to the system/lib directory. First enter adb shell and run mount to view the file system :... /Dev/block/mtdblock0/SystemYaffs2 ro 0 0/dev/block/mtdblock1/data yaffs2 rw, nosuid, nodev 0 0... as you can see,/system depends on/dev/block/mtdblock0 (different devices may be different). Run the following command by referring to the red line: # mount-o remount-rw/dev/block/mtdblock0 system to change the system directory attributes. 2. compile in the android source code Environment1 ). add the C/C ++ source file to the android source code directory, for example, add hello. c To android_sdk/frameworks/base/jnitest2 ). modify android. MK file local_module: = libjni-test local_src_files: = hello. C # local_prelink_module: = false local_c_includes + = $ (jni_h_include) Scheme: = libcutils # local_ldlibs: =-llog include $ (build_shared_library) • specifies whether the dynamic library needs to add ing information in advance. The Android system provides a ing mode for dynamic libraries. In this mode, library files can be loaded in a faster way. The specific need to modify the build/CORE/prelink-linux-arm.map information, specify the address of the dynamic library, such as libjni-test.so 0x9a000000 if you do not need to address ing, need in Android. make the following settings in MK: local_prelink_module: = false • Add the JNI header file path required for compilation and other dependent dynamic libraries local_c_includes + =$ (jni_h_include) local_shared_libraries: = libcutils (print log information) 3 ). you can modify the C/C ++ source code to include androidruntime. then, you can directly call androidruntime: registernativemethods for function registration and use the android internal logcat for information output. 4 ). compile the entire android source code, if the compilation is successful, you can find the successfully compiled libjni-test.so file in out/target/product/generic/system/lib 5 ). system. IMG replaces the system used by the simulator or device. IMG, for example, simulator replacement path: Android-SDK-Windows \ platforms \ Android-7 \ images) use Java to call C/C ++. Process summary: Figure 2 Use C/C ++ to call JavaTo call Java in C/C ++ In the android library file, follow these steps: 1. Get the class definition of the specified object (jclass)There are two ways to get the class definition of an object: 1. Use findclass to find the corresponding class when the class name is known. Note that the class name is not the same as the Java code written at ordinary times. For example, you need to get the class JNI. test. the demo definition must call the following code: // change the dot to the slash jclass CLS = (* env)-> findclass (ENV, "Sam/test/jnitest"); 2. get the corresponding class definition through the passed parameter object // Where obj is the object to be referenced and the type is jobject jclass CLS = (* env)-> getobjectclass (ENV, OBJ ); 2. Read the definition of the method to be called (jmethodid)Let's take a look at Android JNI. function for getting method definitions in H: jmethodid (* getmethodid) (jnienv *, jclass, const char *, const char *); jmethodid (* getstaticmethodid) (jnienv *, jclass, const char *, const char *); the difference between the two functions is that getstaticmethodid is used to obtain the definition of static methods, while getmethodid is used to obtain non-static method definitions. Env is the JNI environment, the second parameter class is the class definition of the object, the third parameter is the method name, and the fourth parameter is the definition of the method, for method definition rules, refer to the introduction in the previous section [Create a ing table between JNI and jnitest classes. •/* Suppose we already have a Sam. test. jnitest. jnitest instance OBJ * // get the class definition of the Instance jclass javacls = (* env)-> getobjectclass (ENV, OBJ); jmethodid mid = (* env) -> getmethodid (ENV, javacls, "getjavamessage", "() ljava/lang/string;"); If (mid = 0) return; 3. Call object methods and attributes1. Call the object method. To call a method of an object, you can use the callxxxxmethod function or callstaticxxxxmethod (static method of the callback class) based on different return types. For example: callintmethod, callcharmethod, callstaticvoidmethod in this example, call a method that returns the string type, call the following: jstring MSG = (* env)-> callobjectmethod (ENV, OBJ, mid ); /* If the method is a static method, you only need to write the last Code as follows: jstring MSG = (* env)-> callstaticobjectmethod (ENV, javacls, mid ); */2. read and set attribute values

The attributes of the category class are basically the same as those of the category class, except that the method is changed to an attribute. There are several methods used to read and set the attributes of the class. They are:

Getfield, setfield, getstaticfield, and setstaticfield. For example, getobjectfield can be used to read the strmsg attribute of the jnitest class. The related code is as follows: jclass javacls = (* env)-> getobjectclass (ENV, OBJ); jfieldid field = (* env) -> getfieldid (ENV, javacls, "strmsg", "ljava/lang/string;"); jstring msgfield = (* env)-> getobjectfield (ENV, OBJ, field ); you can also change the attributes of a class :(* Env)-> Setobjectfield ( Env, OBJ, Field ,(* Env)-> Newstringutf ( Env, "Changed to C string ")); Iv. Handling exceptionsWhen calling Java in C/C ++, capture and handle the exception information thrown by the Java method. The exception should be checked after each method call: MSG = (jstring) ENV-> callobjectmethod (OBJ, mid); If (env-> exceptionoccurred ()) {env-> predictiondescribe (); env-> predictionclear (); return ;} End 
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.