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:
Android JNI QQ funny voice combat (including complete Demo)
The complete code
Original is not easy, your praise is my biggest support ~