First knowledge of Android JNI (1)

Source: Internet
Author: User

The Java Native Interface (JNI) standard is part of the java platform. It allows Java code to interact with code written in other languages. JNI is a local programming interface that enables the Java code running inside the Java Virtual Machine (VM) to work with other programming languages (such as C, C ++, and assembly languages) write the application program and the library for interactive operations.

1. Starting from how to load the. so file

Because the classes at the Android Application layer are written in Java, after these Java classes are compiled into Dex-type Bytecode, they must be executed by the Dalvik Virtual Machine (VM: Virtual Machine. VM plays an important role in the Android platform.
In addition, during Java class execution, if the Java class needs to communicate with the C component, the VM will load the C component, then, the Java functions can be smoothly tuned to the C component functions. in this case, the VM acts as a bridge, allowing Java and C components to communicate with each other through the standard JNI interface.
Java classes at the application layer are executed on virtual machines (VM: Vitual Machine), while C is not executed on the VM. How can Java programs require VM to Load) what about the specified C component? You can use the following commands:
System. loadLibrary (*. so file name );

For example, the MediaPlayer. java class provided in the Android framework contains commands:

Java code:
Publicclass MediaPlayer {
Static {
System. loadLibrary ("media_jni ");
}
}

This requires the VM to load the/system/lib/libmedia_jni.so file of Android. After loading *. so, the Java class and *. so file will be merged and executed together.

2. How to Write the entry function *. so

---- Functions of JNI_OnLoad () and JNI_OnUnload ()
When the Android VM (Virtual Machine) executes the System. loadLibrary () function, it first executes the JNI_OnLoad () function in the C component. It has two purposes:
(1) Tell VM that the C component uses the JNI version. if your *. the so file does not provide the JNI_OnLoad () function. VM will default this *. the so file uses the oldest JNI 1.1 version. because the new version of JNI has made a lot of expansion, if you need to use the new version of JNI features, such as JNI 1.4 java. nio. byteBuffer, The JNI_OnLoad () function must be used to notify the VM.
(2) because the VM runs to System. when the loadLibrary () function is used, JNI_OnLoad () is called immediately. Therefore, the C component developer can use JNI_OnLoad () to set the initial value (Initialization) in the C component ).
For example, in the/system/lib/libmedia_jni.so file of Android, The JNI_OnLoad () function is provided, and its code segment is:

Java code:
// # Define LOG_NDEBUG 0
# Define LOG_TAG "MediaPlayer-JNI"
Jint JNI_OnLoad (JavaVM * vm, void * reserved ){

JNIEnv * env = NULL;
Jint result =-1;


If (vm-> GetEnv (void **) & env, JNI_VERSION_1_4 )! = JNI_ OK ){

LOGE ("ERROR: GetEnv failed \ n ");
Goto bail;
}

Assert (env! = NULL );
If (register_android_media_MediaPlayer (env) <0 ){
LOGE ("ERROR: MediaPlayer native regiled failed \ n ");
Goto bail;
}

If (register_android_media_MediaRecorder (env) <0 ){
LOGE ("ERROR: MediaRecorder native regiled failed \ n ");
Goto bail;
}

If (register_android_media_MediaScanner (env) <0 ){
LOGE ("ERROR: MediaScanner native regiled failed \ n ");
Goto bail;
}

If (register_android_media_MediaMetadataRetriever (env) <0 ){
LOGE ("ERROR: MediaMetadataRetriever native regiled failed \ n ");
Goto bail;
}

/* Success -- return valid version number */
Result = JNI_VERSION_1_4;
Bail:
Return result;
}

This function returns the JNI_VERSION_1_4 value to the VM, so the VM knows the JNI version it uses. in addition, it also performs some initial actions (can call any local function), such as commands:

Java code:
If (register_android_media_MediaPlayer (env) <0 ){
LOGE ("ERROR: MediaPlayer native regiled failed \ n ");
Goto bail;
}
Copy code
Register the Native functions provided by this component to the VM to accelerate subsequent calls to the local functions.
The JNI_OnUnload () function corresponds to JNI_OnLoad. When the C component is loaded, JNI_OnLoad () is immediately called for initial actions in the component. When the VM releases the C component, JNI_OnUnload () is called () function. When the VM calls the JNI_OnLoad () or JNI_Unload () function, it will pass the VM Pointer (Pointer) to them. The parameters are as follows:

Java code:
Jint JNI_OnLoad (JavaVM * vm, void * reserved ){}

Jint JNI_OnUnload (JavaVM * vm, void * reserved ){}

In the JNI_OnLoad () function, the JNIEnv indicator value is obtained through VM indicators and stored in the env indicator variable, as instructed below:

Java code:
Jint JNI_OnLoad (JavaVM * vm, void * reserved ){
JNIEnv * env = NULL;
Jint result =-1;
If (vm-> GetEnv (void **) & env, JNI_VERSION_1_4 )! = JNI_ OK ){
LOGE ("ERROR: GetEnv failed \ n ");
Goto bail;
}
}

 


Because the VM is usually the execution environment of Multi-threading. When JNI_OnLoad () is called by each thread, the passed JNIEnv metric values are different. To work with this multi-thread environment, C component developers can avoid conflicts in execution threads by using different JNIEnv metric values when writing local functions, in order to ensure that the written local functions can be safely executed in the Android multi-thread VM. For this reason, when calling a function of the C component, the JNIEnv indicator value is passed to it, as shown below:

Java code:
Jint JNI_OnLoad (JavaVM * vm, void * reserved ){

JNIEnv * env = NULL;
If (register_android_media_MediaPlayer (env) <0 ){
}
}
Copy code
When JNI_OnLoad () calls the register_android_media_MediaPlayer (env) function, the env indicator value is passed. In this way, the register_android_media_MediaPlayer () function can distinguish different execution threads with this indicator value to resolve data conflicts.

For example, in the register_android_media_MediaPlayer () function, you can write the following commands:

If (* env)-> MonitorEnter (env, obj )! = JNI_ OK ){
}
Check whether other threads have entered the object. If no thread exists, the thread is executed in the object. You can also write the following commands:
If (* env)-> MonitorExit (env, obj )! = JNI_ OK ){
}

Check whether the thread is being executed in this object. If yes, the thread will leave immediately.

3. Purpose of the registerNativeMethods () function

The Java class at the application level calls local functions through the VM. generally, it depends on the VM to find *. so local function. if you need to call multiple times in a row, it will take a lot of time to search for them each time. in this case, the component developer can register the local function with the VM. for example, the code segment in the/system/lib/libmedia_jni.so file of Android is as follows:

Java code:
// # Define LOG_NDEBUG 0

# Define LOG_TAG "MediaPlayer-JNI"

Static JNINativeMethod gMethods [] = {

{"SetDataSource", "(Ljava/lang/String;) V ",

(Void *) android_media_MediaPlayer_setDataSource },

{"SetDataSource", "(Ljava/io/FileDescriptor; JJ) V ",

(Void *) android_media_MediaPlayer_setDataSourceFD },

{"Prepare", "() V", (void *) android_media_MediaPlayer_prepare },

{"PrepareAsync", "() V", (void *) android_media_MediaPlayer_prepareAsync },

{"_ Start", "() V", (void *) android_media_MediaPlayer_start },

{"_ Stop", "() V", (void *) android_media_MediaPlayer_stop },

{"GetVideoWidth", "() I", (void *) android_media_MediaPlayer_getVideoWidth },

{"GetVideoHeight", "() I", (void *) android_media_MediaPlayer_getVideoHeight },

{"SeekTo", "(I) V", (void *) android_media_MediaPlayer_seekTo },

{"_ Pause", "() V", (void *) android_media_MediaPlayer_pause },

{"IsPlaying", "() Z", (void *) android_media_MediaPlayer_isPlaying },

{"GetCurrentPosition", "() I", (void *) android_media_MediaPlayer_getCurrentPosition },

{"GetDuration", "() I", (void *) android_media_MediaPlayer_getDuration },

{"_ Release", "() V", (void *) android_media_MediaPlayer_release },

{"_ Reset", "() V", (void *) android_media_MediaPlayer_reset },

{"SetAudioStreamType", "(I) V", (void *) android_media_MediaPlayer_setAudioStreamType },

{"SetLooping", "(Z) V", (void *) android_media_MediaPlayer_setLooping },

{"SetVolume", "(FF) V", (void *) android_media_MediaPlayer_setVolume },

{"GetFrameAt", "(I) Landroid/graphics/Bitmap ;",

(Void *) android_media_MediaPlayer_getFrameAt },

{"Native_setup", "(Ljava/lang/Object;) V ",

(Void *) android_media_MediaPlayer_native_setup },

{"Native_finalize", "() V", (void *) android_media_MediaPlayer_native_finalize },

};

Staticint register_android_media_MediaPlayer (JNIEnv * env ){
Return AndroidRuntime: registerNativeMethods (env, "android/media/MediaPlayer", gMethods, NELEM (gMethods ));
}

Java code:
Jint JNI_OnLoad (JavaVM * vm, void * reserved ){

If (register_android_media_MediaPlayer (env) <0 ){

LOGE ("ERROR: MediaPlayer native regiled failed \ n ");
Goto bail;
}
}
Copy code
When the VM loads the libmedia_jni.so file, it calls the JNI_OnLoad () function. then, JNI_OnLoad () calls the register_android_media_MediaPlayer () function. now, call the AndroidRuntime: registerNativeMethods () function to register the local functions contained in the gMethods [] table with the VM (AndroidRuntime. in short, the registerNativeMethods () function has two purposes:

(1) Find functions more efficiently.
(2) It can be switched during execution. gMethods [] is a <name, function pointer> table. During program execution, you can call registerNativeMethods () function multiple times to replace the pointer of the local function, to achieve the purpose of using local functions elastically.

4. 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:

Java code:
Typedef struct {

Constchar * name;/* name of the function in Java */

Constchar * signature;/* describes the function parameters and return values */

Void * fnPtr;/* function pointer, pointing to C function */

} JNINativeMethod;
Copy code
"() V"
"(II) V"
"(Ljava/lang/String;) V"
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 relationship between each character is as follows:
Character Java type C type

Java code:
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
Copy code
The array starts with "[" and is represented by two characters.

Java code:
[I jintArray int []

[F jfloatArray float []

[B jbyteArray byte []

[C jcharArray char []

[S jshortArray short []

[D jdoubleArray double []

[J jlongArray long []

[Z jbooleanArray boolean []
Copy code
The above are all basic types. 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, and its corresponding class is jstring.

Ljava/lang/String; String jstring
Ljava/net/Socket; Socket jobject
If a 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"

Android JNI programming practices

1. directly use the java jni interface (windows/ubuntu)
1. Create an android Application in cmdsh. two classes: one inherited from the Activity and the UI is displayed. The other contains the native method. compile and generate all classes.
Jnitest. java file:

Java code:
Import android. app. Activity;
Import android. OS. Bundle;

Publicclass jnitest extends Activity {

/** Called when the activity is first created .*/

@ Override
Publicvoid onCreate (Bundle savedInstanceState ){

Super. onCreate (savedInstanceState );

SetContentView (R. layout. main );

Nadd cal = new Nadd ();

SetTitle ("The Native Add Result is" + String. valueOf (cal. nadd (10, 19 )));

}

}

// Nadd. java file:
Publicclass Nadd {

Static {
System. loadLibrary ("Nadd ");
}
Publicnativeint nadd (int a, int B );
}

The preceding steps are completed in windows.

2. use the javah command to generate C/C ++. h file. note that the class should contain the package name, and the path folder should contain classes in all packages. Otherwise, an error will be reported that the class cannot be found. the classpath parameter specifies the first-level folder before the package name. The folder hierarchy must conform to the java class organization hierarchy.
Javah-classpath/jnitest/bin com. hello. jnitest. Nadd
Com_hello_jnitest_Nadd. h file:

Java code:
/* Do not edit this file-it is machine generated */
# Include <jni. h>
/* Header for class com_hello_jnitest_Nadd */
# Ifndef _ Included_com_hello_jnitest_Nadd

# Define _ Included_com_hello_jnitest_Nadd

# Ifdef _ cplusplus

Extern "C "{

# Endif

/*
* Class: com_hello_jnitest_Nadd
* Method: nadd
* Signature: (II) I
*/

JNIEXPORT jint JNICALL Java_com_hello_jnitest_Nadd_nadd (JNIEnv *, jobject, jint, jint );
# Ifdef _ cplusplus
}

# Endif

# Endif
Copy code
3. Edit the. c file to implement the native method.
Com_hello_jnitest_Nadd.c file:

Java code:
# Include <stdlib. h>
# Include "com_hello_jnitest_Nadd.h"
JNIEXPORT jint JNICALL Java_com_hello_jnitest_Nadd_nadd (JNIEnv * env, jobject c, jint a, jint B ){
Return (a + B );
}
Copy code
4. Compile the. c file to survive the dynamic library.
Arm-none-linux-gnueabi-gcc-I/home/a/work/android/jdk1.6.0 _ 17/include-I/home/a/work/android/jdk1.6.0 _ 17/ include/linux-fpic-c com_hello_jnitest_Nadd.c
Arm-none-linux-gnueabi-ld-T/home/a/CodeSourcery/Sourcery_G ++ _ Lite/arm-none-linux-gnueabi/lib/ldscripts/armelf_linux_eabi.xsc-share- o libNadd. so com_hello_jnitest_Nadd.o
Obtain the libNadd. so file.
The preceding steps are completed in ubuntu.

5. push the corresponding dynamic library file to system/lib of avd: adb push libNadd. so/system/lib. if the Read-only file system error is prompted, run the adb remount command.
Adb push libNadd. so/system/lib

6. Run the original application in cmdsh.
The preceding steps are completed in windows.
You can also use the second method to compile the so file generated in step 1 into the apk package. you only need to create the libs \ armeabi folder in the project folder (the other folder names are invalid, and only the libs folder is created is invalid), and then upload the so file to compile the project.

Ii. Use NDK to generate a local method (ubuntu and windows)

1. Install NDK: unzip, then enter the directory after NDK unzipping, run build/host-setup.sh (Make 3.81 and awk required). If there is something wrong, modify the host-setup.sh file: Will #! /Bin/sh #! /Bin/bash: Run it again.

2. Create your own project folder under the apps folder, and then create the Application. mk and project folders under the folder.
Application. mk file:
APP_PROJECT_PATH: = $ (call my-dir)/project
APP_MODULES: = myjni

3. create a jni folder under the project folder, and then create a new Android. mk and myjni. c. you do not need to use javah to generate corresponding. h file, but the function name must contain the corresponding complete package and class name.

4. Edit the content of the file.
Android. mk file:

Java code:
# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License ");
# You may not use this file before t in compliance with the License.
# You may obtain a copy of the License
#
# Http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# Distributed under the License is distributed on an "as is" BASIS,
# Without warranties or conditions of any kind, either express or implied.
# See the License for the specific language governing permissions and
# Limitations under the License.
#

LOCAL_PATH: = $ (call my-dir)
Include $ (CLEAR_VARS)
LOCAL_MODULE: = myjni
LOCAL_SRC_FILES: = myjni. c
Include $ (BUILD_SHARED_LIBRARY)
Copy code
Myjni. c file:

Java code:
# Include <string. h>
# Include <jni. h>
Jstring
Java_com_hello_NdkTest_NdkTest_stringFromJNI (JNIEnv * env, jobject thiz ){
Return (* env)-> NewStringUTF (env, "Hello from My-JNI! ");
}
Copy code
Myjni file organization:
A @ ubuntu :~ /Work/android/ndk-1.6_r1/apps $ tree myjni
Myjni
| -- Application. mk
'-- Project
| -- Jni
| -- Android. mk
| '-- Myjni. c
'-- Libs
'-- Armeabi
'-- Libmyjni. so
4 directories, 4 files
5. Compile: make APP = myjni.
The above content is completed in ubuntu. The following content is completed in windows. Of course, it can also be completed in ubuntu.
6. Create an android application in idea Sh. Copy the libs folder automatically generated in myjni to the current project folder and compile and run it.
NdkTest. java file:

Java code:
Import android. app. Activity;

Import android. OS. Bundle;

Import android. widget. TextView;

Publicclass NdkTest extends Activity {

/** Called when the activity is first created .*/

@ Override

Publicvoid onCreate (Bundle savedInstanceState ){

Super. onCreate (savedInstanceState );

TextView TV = new TextView (this );

TV. setText (stringFromJNI ());

SetContentView (TV );

}

Publicnative String stringFromJNI ();

Static {

System. loadLibrary ("myjni ");

}

}


Author: t80t90s

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.