current position:Home>Analyzing Android JNI mechanism

Analyzing Android JNI mechanism

2022-01-26 23:55:29 Karson Tiger

One 、JNI summary

        Android If the system is divided by language, it consists of two worlds , Namely Java The world and Native The world . So why do we have to do this ?Android System consists of Java Can't you write it well ? In addition to performance , The main reason is that Java Before the birth of , There are many programs and libraries that are created by Native Written language , therefore , Reuse these Native Language library is very necessary , Besides, Native The library written in this language has better performance .

        So there's a problem ,Java How to use the world's code Native The code of the world , This requires a bridge to connect them together , and JNI This is the bridge .

                                                

Two 、JNI Methods registration

2.1 Static registration

2.2 Dynamic registration

3、 ... and 、 Type conversion

3.1 Conversion of data types

3.1.1 Conversion of basic data types

Java Native Signature
byte jbyte B
char jchar C
double jdouble D
float jfloat F
int jint I
short jshort S
long jlong J
boolean jboolean Z
void void V

         It can be seen from the above table , Basic data type conversion , except void, Other data types only need to be preceded by “j” That's all right. . In the third column Signature Representative signature format , It will be introduced later . Next, let's look at the conversion of reference data types .

3.1.2 Conversion of reference data type

Java Native Signature
All objects jobject L+classname +;
Class jclass Ljava/lang/Class;
String jstring Ljava/lang/String;
Throwable jthrowable Ljava/lang/Throwable;
Object[] jobjectArray [L+classname +;
byte[] jbyteArray [B
char[] jcharArray [C
double[] jdoubleArray [D
float[] jfloatArray [F
int[] jintArray [I
short[] jshortArray [S
long[] jlongArray [J
boolean[] jbooleanArray [Z

         As can be seen from the table above , Array of JNI The layer data type needs to be “Array” ending , The signature format will start with “[”. Except for arrays , The signature format of other reference data types will be in “;” ending . 

        Inheritance relationship of reference type :

        

Four 、 Method signature

         The signature formats of data types have been listed in the previous table , Method signature consists of signature format , that , What's the use of method signatures ? Let's look at the following code .

4.1 JNI Layer method

        frameworks/base/media/jni/android_media_MediaRecorder.cpp

static const JNINativeMethod gMethods[] = {
  ...
    {"native_init",       "()V",        (void *)android_media_MediaRecorder_native_init},
    {"native_setup",      "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V",
    (void *)android_media_MediaRecorder_native_setup},
   ...
};

         explain :

        gMethods The array stores MediaRecorder Of Native Methods and JNI The correspondence of layer methods ;

         among ”()V” and “(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V” It's a method signature ;

         We know Java There are overload methods , You can define methods with the same name , But the parameters are different , Because of that , stay JNI Cannot be found by method name alone Java The specific method of ;

        JNI To solve this problem, the parameter type and the return value type are combined together as the method signature . Through the method signature and method name, we can find the corresponding method Java Method . 

        JNI The format of the method signature for is : 

( Parameter signature format ...) Return value signature format 

4.2 Java Layer method

         Take it gMethods Array of native_setup Method examples , He was in Java Is defined as follows : 

private native final void native_setup(Object mediarecorder_this,
        String clientName, String opPackageName) throws IllegalStateException;

         It's in JNI The method signature in is :

(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V

       explain :

         Refer to the type conversion table in this article ,native_setup The signature of the first parameter of the method is “Ljava/lang/Object;”, The signature of the last two parameters is “Ljava/lang/String;”, return type void The signature of is “V”, The combination is the above method signature .

4.3 Automatic generation of method signature

(1) If we write every time JNI Write the method signature when writing , It will also be a headache , and Java Provides javap Command to automatically generate method signatures . Let's write a simple one first MediaRecorder.java Including the above native_setup Method :

public class MediaRecorder {//Java layer 
    static {
        System.loadLibrary("media_jni");
        native_init();
    }
    private static native final void native_init();
    private native final void native_setup(Object mediarecorder_this,
        String clientName, String opPackageName) throws IllegalStateException;
}

(2) My local address for this file is D:/Android/MediaRecorder.java, Then execute the following command :

javac D:/Android/MediaRecorder.java

(3) It is generated after the command is executed MediaRecorder.class file , Finally using javap command :

javap -s -p D:/Android/MediaRecorder.class

         among s Represents the output internal type signature ,p Indicates that all methods and members are printed ( Default printing public member ), In the end in cmd The print results in are as follows :

   

         You can see the output clearly native_setup The signature of the method is consistent with that given previously . 

5、 ... and 、JNIEnv

5.1 summary

        JNIEnv It's a point to all JNI Pointer to method , This pointer is only valid on the thread that created it , Cannot pass... Across threads , therefore , Different threads JNIEnv It's independent of each other ;

5.2 effect

         There are two main functions :
                1. call Java Methods .
                2. operation Java( obtain Java Variables and objects in ).

5.3 JNIEnv The definition of

         Code location :libnativehelper/include/nativehelper/jni.h

#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;//C++ in JNIEnv The type of  
typedef _JavaVM JavaVM; 
#else
typedef const struct JNINativeInterface* JNIEnv;//C in JNIEnv The type of   
typedef const struct JNIInvokeInterface* JavaVM;
#endif

        explain :

         The predefined macro is used here __cplusplus To distinguish between C and C++ Two codes , If you define __cplusplus, It is C++ Definition in code , Otherwise, it would be C Definition in code .

         And we see that here JavaVM, It 's a virtual machine in JNI Representation of layers , There is only one in a virtual machine process JavaVM, therefore , This is available to all threads of the process JavaVM.

         adopt JavaVM Of AttachCurrentThread Function to get the JNIEnv, This can be invoked in different threads. Java The method . And remember to use it AttachCurrentThread Function before thread exit , Be sure to call DetachCurrentThread Function to free resources .

5.4 jfieldID and jmethodID 

5.4.1 Access method

         stay JNI of use jfieldID and jmethodID To represent the Java Member variables and methods in a class , Can pass JNIEnv The following two methods to get the :

//JNI layer 
jfieldID  GetFieldID(jclass clazz,const char *name,const char *sig);
jmethodID  GetFieldID(jclass clazz,const char *name,const char *sig);

         among ,jclass representative Java class ,name Represents the name of a member method or member variable ,sig Sign the method and variable .

5.4.2 obtain jfieldID and jmethodID

         So let's look at MediaRecorder Framework of the JNI layer How to use the above two methods , As shown below :

         Code location :frameworks/base/media/jni/android_media_MediaRecorder.cpp

//JNI layer 
static void
android_media_MediaRecorder_native_init(JNIEnv *env)
{
    jclass clazz;
    clazz = env->FindClass("android/media/MediaRecorder");//1
    if (clazz == NULL) {
        return;
    }
    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");//2
    if (fields.context == NULL) {
        return;
    }
    fields.surface = env->GetFieldID(clazz, "mSurface", "Landroid/view/Surface;");//3
    if (fields.surface == NULL) {
        return;
    }
    jclass surface = env->FindClass("android/view/Surface");
    if (surface == NULL) {
        return;
    }
    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");//4
    if (fields.post_event == NULL) {
        return;
    }
}

        explain :

         notes 1 It's about , adopt FindClass To find the Java Layer of MediaRecorder Of Class object , And assign it to jclass Variable of type clazz, therefore ,clazz Namely Java Layer of MediaRecorder stay JNI Representation of layers .

         notes 2 And comments 3 The code at is used to find Java Layer of MediaRecorder Middle name is mNativeContext and mSurface Member variables of , And assign them to context and surface.

         notes 4 Get Java Layer of MediaRecorder Middle name is postEventFromNative Static method of , And assign it to post_event. among fields For the definition of :

struct fields_t {//JNI layer 
    jfieldID    context;
    jfieldID    surface;
    jmethodID   post_event;
};
static fields_t fields;

         Assign these member variables and methods to jfieldID and jmethodID The type of variable is mainly for efficiency , If you need to query methods and variables every time you call related methods , It's obviously inefficient , So in MediaRecorder frame JNI Layer initialization method android_media_MediaRecorder_native_init in Will these jfieldID and jmethodID Save variables of type , For future use .

5.4.3 Use jfieldID and jmethodID

        We saved jfieldID and jmethodID Variable of type , Then how do you use them , As shown below : 

        1. Use jmethodID

        Code location :frameworks/base/media/jni/android_media_MediaRecorder.cpp

void JNIMediaRecorderListener::notify(int msg, int ext1, int ext2)
{//JNI layer 
    ALOGV("JNIMediaRecorderListener::notify");

    JNIEnv *env = AndroidRuntime::getJNIEnv();
    env->CallStaticVoidMethod(mClass, fields.post_event, mObject, msg, ext1, ext2, NULL);//1
}

        explain :

         In the comments 1 Called JNIEnv Of CallStaticVoidMethod function , Among them, it was introduced fields.post_event, We know from the above that , It's actually preserved Java layer MediaRecorder Static method of postEventFromNative;

        postEventFromNative Method :

        Code location :frameworks/base/media/java/android/media/MediaRecorder.java

private static void postEventFromNative(Object mediarecorder_ref,
                                        int what, int arg1, int arg2, Object obj)
{//Java layer 
    MediaRecorder mr = (MediaRecorder)((WeakReference)mediarecorder_ref).get();
    if (mr == null) {
        return;
    }
    if (mr.mEventHandler != null) {
        Message m = mr.mEventHandler.obtainMessage(what, arg1, arg2, obj);
        mr.mEventHandler.sendMessage(m);
    }
}

         So that we can be in JNI Layer access Java It's a static method , Empathy , If you want to visit Java The method can be used JNIEnv Of CallVoidMethod function .

        2. Use jfieldID

        Code location :frameworks/base/media/jni/android_media_MediaRecorder.cpp

static void
android_media_MediaRecorder_prepare(JNIEnv *env, jobject thiz)
{//JNI layer 
    ALOGV("prepare");
    sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
    jobject surface = env->GetObjectField(thiz, fields.surface);//1
    if (surface != NULL) {
        const sp<Surface> native_surface = get_surface(env, surface);
       ...
    }
    process_media_recorder_call(env, mr->prepare(), "java/io/IOException", "prepare failed.");

        explain :

         In the comments 1 Called JNIEnv Of GetObjectField function , In the parameter fields.surface For preservation Java layer MediaRecorde Member variables in mSurface,mSurface The type of Surface, This way GetObjectField The function gets the result mSurface stay JNI The corresponding jobject Type variable surface .

Reference material :

|( detailed ): label : Android In depth understanding of JNI | BATcoder - Liu Wangshu

| Android JNI Principle analysis - Gityuan Blog | Yuan Huihui's technology blog

| Android Framework layer JNI Analysis on the use of - Android Development - Development languages and tools - Deep open source

| Android Of JNI Function registration

| jni introduces :Android IO monitor | Performance monitoring series - Nuggets

copyright notice
author[Karson Tiger],Please bring the original link to reprint, thank you.
https://en.cdmana.com/2022/01/202201262355271962.html

Random recommended