Android-simple JNI instance

Source: Internet
Author: User

1. JNI Overview
JNI is the abbreviation of Java Native Interface. It is translated into "Java local call" in Chinese, and 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. That is to say, JNI is a technology, which can achieve two points:
1) functions in Java programs can call functions written in Native. Native generally refers to functions written in C/C ++.

2) functions in Native programs can call functions at the Java layer, that is, C/C ++ programs can call Java functions.


Ii. JNI Application

1. JNI local call: (for example)

Java :( HelloWorld. java)

class HelloWorld {private native void print();public static void main(String[] args){new HelloWorld().print();}static {System.loadLibrary("HelloWorld");}}

Create a new java file. We can see that there is a native in the second line, which indicates that this function is used in java and other languages (c/c ++) for collaboration and is implemented in other languages. The file contains System. loadLibrary, which is used to load local methods. In principle, it can be loaded anytime and anywhere before the native function is called. Its parameter is the name of the dynamic library.
Then, use the javac HelloWorld. java command to generate the HelloWorld. class file,
The next step is to use javah-jni-classpath. helloWorld generates HelloWorld corresponding to C/C ++. h file. The purpose of setting classpath is to tell the java environment which directories can find the classes or packages required by the java program to be executed. Do not declare them here. class file name, which is automatically found by the compiler in the corresponding directory. View. h file:

*******/*  * Class:     HelloWorld  * Method:    print  * Signature: ()V  */ JNIEXPORT void JNICALL Java_HelloWorld_print   (JNIEnv *, jobject);*******

The annotation part is the class, native method, and method signature of the corresponding file. The method signature is composed of the method parameters and return value types, for example. in the H file, Signature :() V, where () represents a method parameter, and V represents a method without a return value. The parameter types and Signature in java programs have different corresponding values. For more information, see them online.
This corresponds to the jni function of the method in java. JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *, jobject); JNIEXPORT and JNICALL are both JNI keywords, indicating that this function is called by JNI.
JNIEnv is a thread-related structure that represents the JNI environment. It is used to call JAVA functions or operate Jobject objects.


The meaning of the second parameter depends on whether the method is static or an instance method. When the local method is used as an instance method, the second parameter is equivalent to the object itself, that is, this, when the local method acts as a static method, it points to the class.
C :( HelloWorld. c)

#include 
 
   #include 
  
    #include "HelloWorld.h" JNIEXPORT void JNICALL Java_HelloWorld_print(JNIEnv *env, jobject obj) { printf("Hello World!\n"); return; }
  
 
In this case, the functions in the. c file must be the same as those in the. h file, so that the corresponding JNI function can be found based on the library during JAVA calling and then called to execute the function.
The following describes how to generate a dynamic library.
$: Gcc-I/usr/lib/jvm/java-6-sun/include/linux/-I/usr/lib/jvm/java-6-sun/include/-fPIC- shared-o libHelloWorld. so HelloWorld. c
Gcc is the compilation command of the c program.-I is used to specify the relevant header file in the path, and the call can be completed by executing java HelloWorld.
2. Call jni in android (eclipse)
(1) Build the Android NDK development environment under ubuntu (refer to my local build method only)
Download NDK, http://dl.google.com/android/ndk/android-ndk-r4b-linux-x86.zip
② Decompress the package to the current directory
$ Pwd <-- view the current directory ~ Android-ndk-r4b
③ Configure Environment Variables
$ Gedit ~ /. Bashrc: Add the following content to the end of the opened file
NDK = ~ Android-ndk-r4d
Export NDK
④ Read and execute in the current environment ~ /. Bashrc command:
$ Source ~ /. Bashrc
⑤ Check whether it takes effect
$ Echo $ NDK
~ /Android-ndk-r4b/
(2) register the JNI Function
① Static registration
Java:
package com.example.hellojni; import android.app.Activity; import android.widget.TextView; import android.os.Bundle; public class HelloJni extends Activity {     @Override     public void onCreate(Bundle savedInstanceState)     {         super.onCreate(savedInstanceState);         TextView  tv = new TextView(this);         tv.setText( stringFromJNI() );         setContentView(tv);     }     public native String  stringFromJNI();     static {         System.loadLibrary("hello-jni");     } }
The. class file is automatically generated in the/bin/classes/com/example/hellojni directory of the project,
In this case, you can use the command javah-jni-classpath bin/classes/com. example. hellojni. helloJni generation. h file. It can be seen that the current folder has a com_example_hellojni_HelloJni.h file consisting of package name and class names,
Create a new jni folder and a new hello-jni.c and Android. mk.
Android. mk

LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE    := hello-jni LOCAL_SRC_FILES := hello-jni.c include $(BUILD_SHARED_LIBRARY)
The Code is as above. android. mk is a makefile provided by android. It is used to specify the so library name generated by compilation and the referenced header file. LOCAL_PATH indicates the path of the current file. LOCAL_MODULE is the module name and must be unique and cannot contain spaces. LOCAL_SRC_FILES indicates the list of source files to be compiled; LOCAL_SHARED_LIBRARIES indicates the shared library required by the module during running and required during connection.
Hello-jni.c

#include 
 
   #include 
  
    jstring Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,                                                   jobject thiz ) {     return (*env)->NewStringUTF(env, "Hello from JNI !"); }
  
 
The function name in this file must be the same as the generated one. the function names in the H file are the same, otherwise they may not be found, that is, Java, the corresponding package name class name and function name constitute the new function name.
Execute ndk:/home/wesley/android-ndk-r4b/ndk-build, generate libraries in the obj file, and load and run all in eclipse to display them in the simulator.
Conclusion: When the Java layer calls the stringFromJNI function, it searches for the Java_com_example_hellojni_HelloJni_stringFromJNI function from the corresponding JNI library. If no, an error is returned. If yes, the stringFromJNI and Java_com_example_hellojni_HelloJni_stringFromJNI will establish an association. In fact, it is to save the function pointer of the JNI layer function. You can directly use this function pointer when calling the stringFromJNI Function, of course, this work is done by virtual machines.
② Dynamic Registration
It can be seen from static registration that it has several drawbacks. First, we need to compile all Java classes that declare native functions. Each generated class file must use javah to generate a header file; secondly, the JNI-layer function name generated by javah is very long, causing inconvenience in writing. when calling the native function for the first time, you need to search for the corresponding JNI-layer function based on the function name to establish an association, which affects efficiency. Therefore, the dynamic registration directly allows native functions to know the function pointer corresponding to the JNI layer.
If the above project is changed to dynamic registration, you only need to change the C file:
HelloWorld. c:
# Include
 
  
# Include
  
   
# Include
   
    
# Include
    
     
Jstring native_hello (JNIEnv * env, jobject thiz) {return (* env)-> NewStringUTF (env, "dynamic JNI registration ");} /** method table */static JNINativeMethod gMethods [] ={{ "stringFromJNI", "() Ljava/lang/String;", (void *) native_hello },}; /** register a local method for a class */static int registerNativeMethods (JNIEnv * env, const char * className, JNINativeMethod * gMethods, int numMethods) {jclass clazz; /* env points to a JNIEnv struct. classname corresponds to the Java class name. Due to JNINativeMethod The function used in is not a full path name, so you must specify the class. */Clazz = (* env)-> FindClass (env, className); if (clazz = NULL) {return JNI_FALSE ;} // In fact, JNIEnv's RegisterNatives function is called to complete registration if (* env)-> RegisterNatives (env, clazz, gMethods, numMethods) <0) {return JNI_FALSE ;} return JNI_TRUE;}/** register a local method for all classes */static int registerNatives (JNIEnv * env) {const char * kClassName = "com/example/hellojni/HelloJni "; return registerNativeMethods (env, kClassName, gMethods, sizeof (gMethods) /Sizeof (gMethods [0]);}/** System. loadLibrary ("lib") is called. If the JNI version is returned successfully,-1 * is returned if the first parameter type of this letter is JavaVM, is the representation of the virtual machine in the JNI Layer * each Java Process has only one such JavaVM */JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM * vm, void * reserved) {JNIEnv * env = NULL; jint result =-1; if (* vm)-> GetEnv (vm, (void **) & env, JNI_VERSION_1_4 )! = JNI_ OK) {return-1;} assert (env! = NULL); // dynamically register the JNI function if (! RegisterNatives (env) {return-1;} result = JNI_VERSION_1_4; return result ;}
    
   
  
 
The JNINativeMethod struct is defined in jni. h. Using this struct, You can map the native function in java to the function name here. So why do we need this signature information? Because Java supports function overloading, that is, functions with the same name but different parameters can be defined. However, no specific function can be found based on the function name. In JNI technology, the parameter type and return value type are combined into the signature information of a function, with the signature information and function name, you can find the functions in Java smoothly.
Typedef struct {// native function name in java, without the use of the package path char * name; // signature information of the Java function. Char * signature; // function pointer void * fnPtr corresponding to the JNI layer;} JNINativeMethod;
The signature in this struct can be obtained by running the javap-p-s HelloWorld command in the directory where the class file is located.
Then load the database to complete dynamic registration.
Conclusion: When the Java layer passes the System. after loadLibrary loads a JNI dynamic library, it will find a function called JNI_OnLoad in the library. If yes, it will be called, and the dynamic registration work will be completed from here.

(3) operate jobject through JNIEnv:
Java objects are composed of member variables and member functions. The essence of jobject operations should be member variables and member functions that operate on these objects. They are class attributes. Therefore, in JNI rules, jfieldID and jmethodID are used to represent the tired member variables and member functions of Java. You can obtain them through the following two functions of JNIEnv:
JfieldID GetFieldID (jclass clazz, const char * name, const char * sig)
JmethodID GetMethodID (jclass clazz, const char * name, const char * sig)
Among them, jclass represents the Java class, name represents the name of the member function or member variable, sig is the signature information of this function and variable. Save the obtained ID as follows to improve efficiency.
(4) garbage collection
① Local Reference: Local Reference. The non-global Reference objects used in the JNI-layer functions are ocal Reference, which includes the jobject and jobject created in the JNI-layer functions when the function is called. The biggest feature of ocal Reference is that once the JNI layer function returns, these jobject may be reclaimed.
② Global Reference: Global Reference. If an object cannot be released, it will never be recycled.
③ Weak Global Reference: Weak Global Reference. A special Global Reference may be reclaimed during running, so before use, the IsSameObject of JNIEnv is required to determine whether the object is recycled.



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.