JNI: Java Native Interface is called locally in JAVA. Why is this technology required? There are two reasons:
1. virtual machines running JAVA programs are written in Native language, while virtual machines run on specific platforms. Therefore, virtual machines cannot be platform independent, the JNI technology can be used to shield different operating system platforms from the JAVA layer, such as file and socket.
2. Before the birth of the JAVA language, many programs were written in Native language, and JAVA directly used JNI to avoid the bad reputation of repeating the wheel. In addition, JNI is more efficient and faster.
Part 1: How to call Native functions in JAVA
The working principle of MediaScanner is explained in full:
Call hierarchy, as shown in the figure below:
JAVA-Layer Code Analysis: MediaScanner. java
Public class MediaScanner
{
Static {// static statement, described in the previous JAVA Learning Series (1)
// Load the JNI library of the application. libmedai_jni.so is called on linux, while libmedia_jni.dll is called on windows.
System. loadLibrary ("media_jni ");
Native_init (); // call the native Function
}
// Non-native Function
Public void scanDirectories (String [] directories, String volumeName)
// Declare the native function. native is the keyword of the JAVA language.
Private static native final void native_init ();
Private native final void native_setup ();
Private native final void native_finalize ();
Private native void processDirectory (String path, String extensions, MediaScannerClient client );
}
Conclusion: one is to load the jni dynamic library, and the other is to declare the native function of java.
Analysis of android_media_MediaScanner.cpp on the jni layer:
First, find the native_init function of the java layer corresponding to the jni layer function?
First, the native_init function is located in the android. media package. The full path is android. media. MediaScanner. native_init.
In the native language, "." has special meanings, so replace it with "_": that is, the path above is android_media_MediaScanner_native_init.
OK, so the function name is associated. JNI function registration can be performed in two ways:
1. Static Registration Method
A. Compile the java code and compile the. class file.
B. Use javah-o output packagename. classname to generate the jni-layer header file
Load the dynamic library before calling, and then find the jni function of the native_init function: android_media_MediaScanner_native_init
If yes, the association between the two functions is established, that is, the function pointer of the jni layer function is saved, which is completed by the virtual machine.
Disadvantage: The function name generated by javah is very long, which is not conducive to writing. You need to search for the function name to create an association for the first call.
2. Dynamic Registration Method
Use the JNINativeMethod structure to save the relationship
Typedef struct
{
// Native function name in JAVA
Const char * name;
// Signature information, represented by a string, a combination of parameter types and return value types
Const char * signature;
/// Function pointer of the JNI layer, converted to void * type
Void * fnPtr;
};
Static JNINativeMethod gMethods [] = {
{"ProcessDirectory", "(Ljava/lang/String; Landroid/media/MediaScannerClient;) V ",
(Void *) android_media_MediaScanner_processDirectory },
{"ProcessFile", "(Ljava/lang/String; Landroid/media/MediaScannerClient;) V ",
(Void *) android_media_MediaScanner_processFile },
{"SetLocale", "(Ljava/lang/String;) V", (void *) android_media_MediaScanner_setLocale },
{"ExtractAlbumArt", "(Ljava/io/FileDescriptor;) [B", (void *) android_media_MediaScanner_extractAlbumArt },
{"Native_init", "() V", (void *) android_media_MediaScanner_native_init },
{"Native_setup", "() V", (void *) android_media_MediaScanner_native_setup },
{"Native_finalize", "() V", (void *) android_media_MediaScanner_native_finalize },
};
The signature information is described below:
Because JAVA supports function overloading, functions with the same name but different parameters can be defined, but specific functions cannot be found directly based on the function name. Therefore, the parameter type and return type are used.
Form signature information.
Common type identifier:
JAVA type Length
Z boolean 8-bit
B byte 8-bit
C char 16-bit -- Note
S short 16-bit
I int 32-bit
J long 64-bit
F float 32-bit
D double 64-bit
L/java/languageString String
[I int [] int Array
[L/java/lang/object Object [] object Array
// This function only registers the native methods, and is called from
// JNI_OnLoad in android_media_MediaPlayer.cpp
Int register_android_media_MediaScanner (JNIEnv * env)
{
// Use registerNativeMethods to register the JNI Function
Return AndroidRuntime: registerNativeMethods (env,
"Android/media/MediaScanner", gMethods, NELEM (gMethods ));
}
// Load the jni library and find the JNI_OnLoad function in the library to complete Dynamic Registration
Jint JNI_OnLoad (JavaVM * vm, void * reserved)
{
...
If (register_android_media_MediaScanner (env) <0 ){
LOGE ("ERROR: MediaScanner native regiled failed \ n ");
Goto bail;
}
}
Static void
Android_media_MediaScanner_processDirectory (JNIEnv * env, jobject thiz, jstring path, jstring extensions, jobject client)
{
MyMediaScannerClient myClient (env, client );
Mp-> processDirectory (pathStr, extensionsStr, myClient, ExceptionCheck, env );
...
}
NATIVE library implementation: MediaScanner. cpp (compiled into libmedia. so Library)
Status_t MediaScanner: processDirectory (
Const char * path, const char * extensions,
MediaScannerClient & client,
ExceptionCheck exceptionCheck, void * exceptionEnv ){
...
Client. setLocale (locale ());
Status_t result =
DoProcessDirectory (
PathBuffer, pathRemaining, extensions, client,
Predictioncheck, predictionenv );
Free (pathBuffer );
...
}
The Calling logic of the entire process is finished.
The second part: Introduction to JNIEnv
JNIEnv is A thread-related variable. Because it is thread-related, the JNIEnv function in thread A cannot be used in thread B.
Who stores multiple threads and ensures that the JNIEnv struct of each thread is correct?
Jint JNI_OnLoad (JavaVM * vm, void * reserved)
The whole process has only one JavaVM object, which can be saved and used anywhere.
Use the AttachCurrentThread function in JavaVM to obtain the JNIEnv struct of this thread, and use DetachCurrnetThread to release corresponding resources.
A. Operate jobject through JNIEnv
JfieldID operation member variable
JmethodID
/*
* Get a field ID (instance fields ).
*/
Static jfieldID GetFieldID (JNIEnv * env, jclass jclazz,
Const char * name, const char * sig)
/*
* Get a method ID for an instance method.
*
* JNI defines <init> as an instance method, but Dalvik considers it
* "Direct" method, so we have to special-case it here.
*
* Dalvik also puts all private methods into the "direct" list, so we
* Really need to just search both lists.
*/
Static jmethodID GetMethodID (JNIEnv * env, jclass jclazz, const char * name,
Const char * sig)
For specific implementation, refer to dalvik \ vm \ jni. c implementation: Important functions include JNI_CreateJavaVM [Create a new VM instance].
Example shows how JNI calls JAVA-layer functions:
JNIEnv * mEnv;
JmethodID mScanFileMethodID;
JmethodID mHandleStringTagMethodID;
MyMediaScannerClient (JNIEnv * env, jobject client)
: MEnv (env ),
MClient (env-> NewGlobalRef (client )),
MScanFileMethodID (0 ),
MHandleStringTagMethodID (0 ),
MSetMimeTypeMethodID (0)
{
...
// Find the android. media. MediaScannerClient class corresponding to the JNI layer and use the jclass instance
Jclass mediaScannerClientInterface = env-> FindClass ("android/media/MediaScannerClient ");
// Retrieve the jMethodID of the scanFile/handleStringTag function in the MediaScannerClient class.
MScanFileMethodID = env-> GetMethodID (mediaScannerClientInterface, "scanFile ",
"(Ljava/lang/String; JJ) V ");
MHandleStringTagMethodID = env-> GetMethodID (mediaScannerClientInterface, "handleStringTag ",
"(Ljava/lang/String;) V ");
}
// Returns true if it succeeded, false if an exception occured in the Java code
Virtual bool scanFile (const char * path, long lastModified, long fileSize)
{
// Call the CallVoidMethod function of JNIEnv:
// The first parameter represents the jobject object of MediaScannerClient, and the second parameter represents the jMethodID of scanFile
// The following is the JAVA-layer ScanFile function parameter.
MEnv-> CallVoidMethod (mClient, mScanFileMethodID, pathStr, lastModified, fileSize );
}
In implementation, JNIEnv provides a series of similar functions to call JAVA functions:
NativeType Call <type> Method (JNIEnv * env, jobject obj, jmethodID methodID ,...)
You can also set or obtain the value of the member variable of the JAVA layer. The definition is as follows:
NativeType Get <type> Field (JNIEnv * env, jobject obj, jfieldID fieldID );
NativeTYpe Set <type> Field (JNIEnv * env, jobject obj, jfieldID fieldID, NativeType value );
From andyhuabing's column