Android JNI advanced

  • Native receives Java to pass basic data and arrays
  • Reference type static/dynamic method calls
  • Advanced reference type
  • Constructor call
  • Global references

Introduction to Android JNI

Native receives Java to pass basic data and arrays

Define the native:

 // Basic type call
public native void nativeBasicTypes(int arg0, double arg2, boolean arg3, String arg4, String[] arg5, int[] arg6);
Copy the code

Define click events:

public void onClick1(View view) {
        String strarray[] = {"Jackie chan".Bruce Lee.Matsui Treasure."Dilieba"."ohh"};
        int ints[] = {1.4.2.62.61};
        nativeBasicTypes(42.24.42.true."Li Yuanba", strarray, ints);

        for (int i = 0; i < ints.length; i++) {
            Log.i("Szjjava layer printing", ints[i] + ""); }}Copy the code

Native code:

extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_activity_AdvancedActivity_nativeBasicTypes(JNIEnv *env, jobject thiz, jint arg0, jdouble arg2, jboolean arg3, jstring arg4, jobjectArray arg5, jintArray arg6) {

    // Accept an int
    int j_int_arg0 = arg0;
    LOGE("Parameter one is %d", j_int_arg0);

    // Accept double
    double j_double_arg2 = arg2;
    LOGE("Parameter two is %f", j_double_arg2);
    
     Bool type 1=true 0=false
    int j_bool_arg3 = arg3;
    LOGE("Parameter three is %d", j_bool_arg3);

    // Accept a String value
	// const char* GetStringUTFChars(jstring string, jboolean* isCopy)
    char *j_string = const_cast<char *>(env->GetStringUTFChars(arg4, NULL));
    LOGE("Parameter four is %s", j_string);

    // Accepts a value of type String[]
    // jsize GetArrayLength(jarray array)
    jsize j_size = env->GetArrayLength(arg5);
    for (int i = 0; i < j_size; i++) {
    
		//jobject GetObjectArrayElement(jobjectArray array, jsize index)
        jstring j_string2 = static_cast<jstring>(env->GetObjectArrayElement(arg5, i));

        // change to a char* type that C++ recognizes
        const char *jstr = env->GetStringUTFChars(j_string2, NULL);
        LOGE("Parameter five is %s", jstr);

        / / release jstring
        env->ReleaseStringChars(j_string2, reinterpret_cast<const jchar *>(jstr));
    }

    //int[] prints the element
    jint *j_ints = env->GetIntArrayElements(arg6, NULL);
    jsize j_size_ints = env->GetArrayLength(arg6);
    for (int i = 0; i < j_size_ints; i++) {
        *(j_ints + i) += 100;
        LOGE("Sixth parameter :%d", *(j_ints + i))
    }
    // Return the modified array to the Java layer
    env->ReleaseIntArrayElements(arg6, j_ints, 0);
}
Copy the code

I’m not going to talk about simple code, I’m going to talk about unfamiliar code;

String description:

  • java -> String
  • JNI -> jstring
  • C++ -> char*

To convert a Java String to a C++ char*, use:

char * j_string = const_cast<char *>(env->GetStringUTFChars(arg4, NULL));
Copy the code

GetObjectArrayElement explanation:

In JNI there are only basic types and reference types. String is a reference type, so when iterating through String[], GetObjectArrayElement() is received

  • Parameter 1 :jobjectArray: All elements
  • Parameter 2 :jsize: current position

ReleaseIntArrayElements explanation:

The ReleaseIntArrayElements() method is C++ telling JNI to change the Java value, passing in an array from the Java layer in the code, and then returning the changes to the Java layer

  • Parameter 1 :jintArray: The array passed by Java
  • Parameter 2: Modified array
  • Parameter 3: modified mode, just fill in 0

ReleaseStringChars explanation:

ReleaseStringChars is used to free JNI memory, although it is ok not to free it because the method ends and the memory is automatically freed once the stack is unloaded. However, if a method has 5000 lines of code waiting for the JNI stack to be freed, it will cause a problem JNI takes up a bit more space when calling this method (good habit)

Running results:

Native running results:2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni E/ SZJ: Parameter one is42
2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni E/ SZJ: Parameter two is24.420000
2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni E/ SZJ: Parameter 3 is1
2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni E/ SZJ: Parameter 4 is Li Yuanba2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni E/ SZJ: Parameter 5 is Jackie Chan2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni E/ SZJ: Parameter 5 is Bruce Lee2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni E/ SZJ: Parameter 5 is Takashi Matsui2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni E/ SZJ: Parameter 5 is Dilieba2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni E/ SZJ: Parameter 5 is ohh2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni E/ SZJ: the sixth parameter is:101
2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni E/ SZJ: the sixth parameter is:104
2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni E/ SZJ: the sixth parameter is:102
2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni E/ SZJ: the sixth parameter is:162
2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni E/ SZJ: the sixth parameter is:161Java runtime results:2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni I/szjjava layer print:101
2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni I/szjjava layer print:104
2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni I/szjjava layer print:102
2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni I/szjjava layer print:162
2021- 04- 28 17:36:22.217 6501- 6501./com.example.jni I/szjjava layer print:161
Copy the code

Reference type static/dynamic method calls

Java code:

public native void nativeMethod(Persion persion);
Copy the code

Auxiliary graph:



The red box on the right is the method to call;

  • void setName()
  • void setAge()
  • static void show(String name)

Native code:

extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_activity_AdvancedActivity_nativeMethod(JNIEnv *env, jobject thiz, jobject persion) {

    /** ** ** * jclass FindClass(const char* name) */
    jclass j_class = env->FindClass("com/example/jni/bean/Persion");

    /** * GetMethodID(jmethodID, const char* name, const char* sig) */
    jmethodID j_method_id = env->GetMethodID(j_class, "getName"."()Ljava/lang/String;");

    // Get the return value of the Persion getName method
    jstring j_strName = static_cast<jstring>(env->CallObjectMethod(persion, j_method_id));

    // change to a char* type that C++ recognizes
    char *j_char_name = const_cast<char *>(env->GetStringUTFChars(j_strName, NULL));
    LOGE("Get value from getName :%s", j_char_name)


    // Get the ID of setName
    jmethodID j_setName_id = env->GetMethodID(j_class, "setName"."(Ljava/lang/String;) V");
    // Get the static show method
    jmethodID j_show_id = env->GetStaticMethodID(j_class, "show"."(Ljava/lang/String;) V");

    env->CallVoidMethod(persion, j_setName_id, env->NewStringUTF("The candy is super sweet."));
    env->CallStaticVoidMethod(j_class, j_show_id, env->NewStringUTF("The candy is super sweet."));
}
Copy the code

There is no strange method in this method, everyone should know, do not understand the comment section oh ~

The running results are as follows:

Native running results:2021- 04- 28 17:44:29.775 6501- 6501./com.example.jni E/ SZJ: Obtain the value from getName: Hong Jingbao2021- 04- 28 17:44:29.775 6501- 6501./com.example.jni I/szjjava layer setName method :: candy super sweet2021- 04- 28 17:44:29.775 6501- 6501./com.example.jni I/szjjava layer show method :: Candy super sweetCopy the code

Advanced reference type

Java defines native methods:

 public native void nativeMethod2(Dog dog);
Copy the code

Auxiliary graph:

Native layer code:

extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_activity_AdvancedActivity_nativeMethod2(JNIEnv *env, jobject thiz, jobject dog_jobj) {

    // Get the Dog Class
    jclass j_dog_class = env->GetObjectClass(dog_jobj);

    // Get the DogShow method ID of the Dog class
    jmethodID jmethodId = env->GetMethodID(j_dog_class, "DogShow"."(Lcom/example/jni/bean/Persion;) V");

    // Get the jclass of Persion
    jclass j_persion_class = env->FindClass("com/example/jni/bean/Persion");
    //AllocObject only instantiates the object and does not call the constructor
    jobject j_persion_job = env->AllocObject(j_persion_class);

    // Assign to Persion
    jmethodID j_setName_id = env->GetMethodID(j_persion_class, "setName"."(Ljava/lang/String;) V");
    jmethodID j_setAge_id = env->GetMethodID(j_persion_class, "setAge"."(I)V");

    // Call setName and setAge of Persion to assign name and age
    env->CallVoidMethod(j_persion_job, j_setName_id, env->NewStringUTF("CAI Xukun"));
    env->CallVoidMethod(j_persion_job, j_setAge_id, 52);

    // Call Dog's DogShow(Persion) method
    env->CallVoidMethod(dog_jobj, jmethodId, j_persion_job);

    // Release the reference types class and job
    env->DeleteLocalRef(j_dog_class);
    env->DeleteLocalRef(j_persion_class);
    env->DeleteLocalRef(j_persion_job);
}
Copy the code

Ideas:

  • Create a Persion object
  • Call Persion’s setName()/setAge() method to assign a value to name/age
  • Creating a Dog object
  • Call DogShow(Persion) on the Dog object and pass Persion in

AllocObject explanation:

AllocObject only instantiates the object and does not call the constructor, which must be used to instantiate the object when a new object is created (not passed in), otherwise the instantiated object will not work

Constructor call

Java defines native methods:

public native void nativeStructure(Dog dog);
Copy the code

Native code:


extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_activity_AdvancedActivity_nativeStructure(JNIEnv *env, jobject thiz, jobject dog) {

    // Get Dog's jclass
    jclass j_dog_class = env->GetObjectClass(dog);

    // Get the constructor
    jmethodID id1 = env->GetMethodID(j_dog_class, "<init>"."()V");
    jmethodID id2 = env->GetMethodID(j_dog_class, "<init>"."(II)V");
    jmethodID id3 = env->GetMethodID(j_dog_class, "<init>"."(III)V");

    // Call the constructor
    env->CallVoidMethod(dog, id1);
    env->CallVoidMethod(dog, id2, 100.200);
    env->CallVoidMethod(dog, id3, 300.400.500);
}
Copy the code

It’s easy to understand:

The constructor is simply <init>, and everything else is the same

Global references

Java definition native code:

// Global reference test
public native void nativeAllQuote(a);
Copy the code

Native code (local reference):

Find the error Find the error

jclass dogClass;// Note that this definition is also a local reference to the outside (unlike Java)
extern "C"
JNIEXPORT void JNICALL
Java_com_example_jni_activity_AdvancedActivity_nativeAllQuote(JNIEnv *env, jobject thiz) {
    if (NULL == dogClass) {
        // This is a local reference (unlike Java)
     dogClass = env->FindClass("com/example/jni/bean/Dog");
    }
    // Get Dog's jclass
    jclass j_dog_class = env->GetObjectClass(dogClass);

    // Get the constructor
    jmethodID id1 = env->GetMethodID(dogClass, "<init>"."()V");
    jmethodID id2 = env->GetMethodID(dogClass, "<init>"."(II)V");
    jmethodID id3 = env->GetMethodID(dogClass, "<init>"."(III)V");

    // Call the constructor
    env->CallVoidMethod(j_dog_class, id1);
    env->CallVoidMethod(j_dog_class, id2, 100.200);
    env->CallVoidMethod(j_dog_class, id3, 300.400.500);
// dogClass = NULL; // Make a comment!
}// When the method ends,dogClass pops onto the stack, but dogClass is still not NULL. At this point, the memory address disappears, but the pointer still points to an internal address, which is the dangling pointer state
Copy the code

The running effect is as follows:

As you can see, when I click the button a second time it crashes;

When the method ends,dogClass will bounce onto the stack, but dogClass is still not NULL. At this time, the memory address disappears, but the pointer still points to an internal address, which is the dangling pointer state

Solution: Turn dogClass into a global reference;

 	const char *a = "com/example/jni/bean/Dog";
        jclass temp = env->FindClass(a);
        // Global reference
        dogClass = static_cast<jclass>(env->NewGlobalRef(temp));
        // Release the temporary jclass
        env->DeleteLocalRef(temp);
Copy the code

Auxiliary graph:

Let’s look at the results:

You can see that there is no problem in how to click, right

But: global references are not recycled automatically, they must be recycled manually!!

Recycle code:

if(dogClass ! =NULL) {
        // Release the global reference
        LOGE("Release global reference")
        env->DeleteLocalRef(dogClass);
        dogClass = NULL;
    }
Copy the code

Auxiliary graph:

Note:

If the so library returns an error, comment out the other two

In the same way, if you want to see JNI basic use (native simp-lib.cpp) code, then JNI advanced and QQ voice practice code comment out!

Only one CPP file can now exist

Other articles by JNI:

Introduction to Android JNI

Android JNI QQ funny voice combat (including complete Demo)

The complete code

Original is not easy, your praise is my biggest support ~