1. Jni basic knowledge

JNI is the abbreviation of Java Native Interface, which means Java Native Interface. This local Interface mainly means that Java can communicate with other programming languages through the local Interface. Sometimes, when developing a certain function, we want to use the accumulated technologies or encapsulated modules. Unfortunately, it wasn’t developed in Java before, so what do you do in this case? A program that has been proven over time cannot be easily rewritten and modified, so JNI is needed as a hub for the program.

  • Jni usage scenarios
  1. When you need to invoke a dependency that the Java language does not support,
  2. Integrate systems developed in non-Java languages, such as C and C++
  3. Save running time and improve running efficiency, such as audio and video
  • Mapping between Jni types and Java types

Since Jni is the bridge between Java and other languages, it must have a basic protocol to communicate with Java code. This foundation is type mapping and signature. Type mapping is to establish a one-to-one correspondence between Java types and types in Jni to achieve type readability and uniqueness. Signature refers to the representation of types, methods, and attributes in Java. The corresponding relationship of methods is searched according to the final signature.

  1. Native method and Jni mapping instance
public static native String action(short s , int i, long l, float f, double d, char c,
                     boolean z, byte b, String str, Object obj, ArrayList<String> array,
                     int[] arr, Action action);
// Generate the corresponding Jni code
JNIEXPORT jstring JNICALL Java_com_alex_kotlin_jni_JniTest_action
  (JNIEnv *, jclass, jshort, jint, jlong, jfloat, jdouble, jchar, jboolean, jbyte, jstring, jobject, jobject, jintArray, jobject);
Copy the code
  1. Basic data mapping relationships
  2. Reference type relational mapping table
  • Jni method name: Java_ class full path _ method name
//native
public native void test(a);
//jni
JNIEXPORT jstring JNICALL Java_com_alex_kotlin_jni_JniTest_test (JNIEnv *, jobject);
Copy the code

Jni (test ()); javah (C); javah (C);

  1. Java: prefix for C++ to implement Java methods
  2. Com_alex_kotlin_jni_JniTest: the full path of the class name of JniTest
  3. Test: native method name
  • Parameters of the rules
  1. JNIEnv * : Entry argument for each native function, executes pointer to JVM function table, JNIEnv can use Java resources in native environment
  2. Jobject: Calls an instance or class object of a Java native method. This parameter is jobject if the native method is a normal method, or jclass if it is static
  3. The remaining parameters are the incoming parameters of native method. Here is the mapping type in JNI (refer to the introduction of mapping relationship)
  • Jni signature
  1. Data type signature: see the comparison table above
  2. Method signature: Combine parameter signatures and return value type signatures as method signatures
JNIEXPORT jstring JNICALL Java_com_alex_kotlin_jni_JniTest_setTest
        (JNIEnv *env, jobject cls, jstring j_str) {} method signature: (Ljava/lang/Object, Ljava/lang/String) Lava/lang/StringCopy the code
1.1 JNI function registration
  • Static registration

Statically registering JNI methods is easy. After we declare native methods in Java, we execute Java commands to compile and generate JNI methods:

javac ***
javah ***
Copy the code

After executing the javah command, the system will create a. H file in the file between. When we call the native method in the Java layer, the system will find the corresponding method according to the JNI method naming rule and the JNI method name, save the JNI pointer after finding the JNI method to establish the connection between methods. Next time call directly use function pointer can, but static registration in the first call need to find and then establish association, affecting efficiency, and static registration is the corresponding dynamic registration, do not need to compile and find association;

  • Dynamic registration
  1. Native methods are implemented in C++ files, where method names are not strictly required
JNIEXPORT jstring JNICALL native_method(JNIEnv *env, jobject) {
    return env->NewStringUTF("Register method in Jni");
};
Copy the code
  1. Create an array of registered methods in which to establish a mapping between Java methods and Jni methods
static JNINativeMethod methods[] = {
    // Parameters: 1, Java declared native method name; 2. Method signature; 3. Method name implemented in c
    {"method"."()Ljava/lang/String;", (void *) native_method},
};
Copy the code
  1. Call the registration method in the overridden JNI_OnLoad () to implement the registration
// Declare the path to dynamically register the corresponding Java class
static const char *const PACKAGE_NAME = "com/alex/kotlin/jni/JniTest";

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) {
    JNIEnv *env;  / / get JNIEnv
    if(JNI_OK ! = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6)) {
        return JNI_ERR;
    }
    jclass jclass1 = env->FindClass(PACKAGE_NAME); // Get jclass by class name
    if (jclass1 == NULL) {
        return JNI_ERR;
    }
    jclassGlobal = static_cast<jclass>(env->NewWeakGlobalRef(jclass1)); // Create global cache jclass
    env->DeleteLocalRef(jclass1); // Release local variables
    if(JNI_OK ! = env->RegisterNatives(jclassGlobal, method,1)) { // Register method
        return JNI_ERR;
    }
    return JNI_VERSION_1_6;
}
Copy the code

JNI_OnLoad () : JNI_OnLoad () : JNI_OnLoad () : JNI_OnLoad (); Finally call the RegisterNatives () method to pass in the jClass and relational array implementation method registration

  • Unannotations method annotations in UnLoad ()
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) ! = JNI_OK) {return;
    }
    env->UnregisterNatives(jclassGlobal); // Unregister
    env->DeleteGlobalRef(jclassGlobal); // Release global variables
}
Copy the code

2. Basic use of Jni

After introducing the basic knowledge of JNI, let’s learn the basic usage of JNI in development, that is, the basic syntax of JNI. In fact, some of the dynamic registration has been used in the above, here is the most common method according to the frequency of use.

2.1. Use of strings
JNIEXPORT jstring JNICALL Java_com_alex_kotlin_jni_JniTest_setTest
        (JNIEnv *env, jobject cls, jstring j_str) {
    const char *c_str = NULL;
    char buff[128] = {};
    jboolean copy;
    c_str = env->GetStringUTFChars(j_str, &copy); // String access
    if(c_str == NULL){ 
        return NULL;
    }
    sprintf(buff, "Jni %s", c_str); // String output
    env->ReleaseStringUTFChars(j_str, c_str); // Release the string
    return env->NewStringUTF(buff); // String creation
}
Copy the code
  • Access string: GetStringUTFChars(j_str, ©)
  1. J_str: the local string to access, where arguments are passed in
  2. Copy: indicates whether a reference is copied. If true is set to copy, false points to a pointer to the source string
  3. Copying a string may fail due to memory problems. After reading the local string, be sure to check if the string is NULL before using it
  4. ReleaseStringUTFChars release string resources, GetStringUTFChars/ReleaseStringUTFChars processing UTF encoded string
  • ReleaseStringUTFChars(j_str, c_str);
  1. After obtaining a character string in C, release the resources. Otherwise, the memory overflows
  2. Usually Get and Release are used in pairs, and different methods are selected for incompatible encodings
  • Create string: NewStringUTF ()
Env - > NewStringUTF (" this is the string!" );Copy the code
  • Other string methods
  1. GetStringChars and ReleaseStringChars: Used to create and release Unicode encoded strings
const jchar *c_str = NULL;
c_str = env->GetStringChars(j_str, &copy);
if (c_str == NULL) {
    return NULL;
}
sprintf(buff,"Jni %s",c_str);  // Cache the string into the buff
env->ReleaseStringChars(j_str, c_str);
Copy the code
  1. GetStringLength: Gets the length of the Unicode encoded string
jsize lenUtf = env->GetStringLength(j_str);
Copy the code
  1. GetStringUTFLength: Gets the length of the UTF-encoded string
jsize lenUtf = env->GetStringUTFLength(j_str);
Copy the code
  1. GetStringCritical and ReleaseStringCritical: Increases the likelihood that the JVM will return a direct pointer to the source string. The previous retrieval string copies and allocates memory. This method reads the string directly without allocating memory, but it cannot call Jni methods and thread blockers on intermediate critical intervals
    const jchar *c_str = NULL;
    char buff[128] = {};
    jboolean copy;
    c_str = env->GetStringCritical(j_str, &copy); // Read the string
    if (c_str == NULL) {
        return NULL;
    }
    sprintf(buff,"Jni %s",c_str);
    env->ReleaseStringCritical(j_str, c_str); // Release the string
    return env->NewStringUTF(buff);
Copy the code
  1. GetStringRegion and GetStringUTFRegion: Intercepts parts of UTF and Unicode format strings, copying the source strings into the cache
JNIEXPORT jstring JNICALL Java_com_alex_kotlin_jni_JniTest_setTest(JNIEnv *env, jobject cls, jstring j_str) {
    char buff[128] = {};
    jsize lenUtf = env->GetStringUTFLength(j_str);   // Reads the length of the string
    env->GetStringUTFRegion(j_str, 0.4, buff);   // Cache string 0 ~ 4 in buff
    return env->NewStringUTF(buff);   // Create a string
}
// Type "From Native! , output result: "From"
Copy the code
2.2. Array manipulation
  • Access the array passed in by Java
Get<PrimitiveType>ArrayElements(ArrayType array, jboolean *isCopy)
Copy the code
  1. If the returned pointer points to the address of the Java array instead of the copy, this will prevent Java from retrieving the array. However, the creation of the copy may fail due to memory problems. Check for NULL before using it
  • Release the array using: ReleaseArrayElements
  1. Fetching and releasing an array must be paired
  2. The last parameter mode in Release can be set in three forms for copying the copy, which can be selected according to whether the array needs to be written back
* 0* JNI_COMMIT: Write the elEMS content back to the Java array, but do not free the elems space. * JNI_ABORT: Writes elEMS content back to Java arrays, freeing elEMS space.Copy the code
  • Using the instance
jint *array ;
jboolean  jboolean1;
array = env->GetIntArrayElements(jintArray1, &jboolean1);  // Get the collection
if (array == NULL){
    return;
}
array[2] = 5;  // Modify the parameters in the collection
env->ReleaseIntArrayElements(jintArray1,array,0); // Release the array collection and write to the Java collection

// Call Jni in Java
int[] number = new int[] {1.2.3.4.5};
Log.e("Before Jni=====",number[2] + "");
test.getAction(number);  // Call the Jni method
Log.e("After Jni=====",number[2] + "");

2019-04-29 22:09:07.698 15989-15989/com.alex.kotlin.jni E/Before Jni=====: 3
2019-04-29 22:09:07.698 15989-15989/com.alex.kotlin.jni E/After Jni=====: 5  // change path 5
/ / use JNI_ABORT
2019-04-29 22:11:55.485 16108-16108/com.alex.kotlin.jni E/Before Jni=====: 3
2019-04-29 22:11:55.485 16108-16108/com.alex.kotlin.jni E/After Jni=====: 3 / / the same
Copy the code

When ReleaseIntArrayElements () is passed 0, the array passed in by the Java layer is changed after the method is executed, which verifies the conclusion that data is written back to Java. JNI_ABORT does not change data in Java.

  • GetArrayLength (j_array) : Obtains the Array length
  • Malloc (len) : Applies for a cache of len length
  • Memset (c_array,0,len) : initializes the array set with len length
  • Memcpy (buffer, data, len) : Copies the specified length of data from an array into a buff array
jbyte* data = env->GetByteArrayElements(javaArray, NULL);
    if(data ! = NULL) { memcpy(buffer, data, len); env->ReleaseByteArrayElements(javaArray, data, JNI_ABORT); }Copy the code
  • GetIntArrayRegion(env,j_array,0, ARR_len,c_array) : copies the specified length of data from the source set to the target set. The function is the same as the preceding block copy function
env->GetByteArrayRegion(javaArray, 0, len, buffer);
Copy the code
  • Accessing an array of objects
  1. FindClass(env,”[I “) : Get Int data reference type
  2. NewObjectArray (env, the size, clsIntArray, NULL) : create an array object
  3. NewIntArray(env,size) : Creates an Int array

C/C++ and Java access

  • C accesses static methods in Java to implement steps
  1. Call FindClass () to get the Class object based on the classpath accessed
  2. Java method MethodId (class, method name, parameter, return value)
  3. Execute the Java method according to class and methodId
public class Action { public static void actionStatic(){ Log.e("=======","Static method from action in Java ") ; JNIEXPORT void JNICALL Java_com_alex_kotlin_jni_JniTest_test (JNIEnv *env, jobject) {jclass CLS = NULL; jmethodID method_Id = NULL; CLS = env - > FindClass (" alex/kotlin/jni/com/Action "); Env ->GetStaticMethodID(CLS, "actionStatic", "()V "); MethodId env->CallStaticVoidMethod(CLS,method_Id); Env ->DeleteGlobalRef(CLS); / / release class} 2019-04-27 17:25:27. 580, 10961-10961 / com. Alex. Kotlin. Jni E / = = = = = = = : Static method from the action in JavaCopy the code
  • To access a Java member method, do the following:
  1. Find the class based on the full path of the class
  2. Find the constructor method methodId based on the method signature
  3. Execute the constructor to create an instance of the class
  4. Find the id of the calling method based on the method name, parameter, and return value
  5. Invoke the corresponding method with the created instance
public class Action { public void action(){ Log.e("=======","Method from action in Java ") ; }} // jclass CLSS = NULL; jmethodID method_Id = NULL; jmethodID construct_Id = NULL; jobject obj = NULL; CLSS = env - > FindClass (" alex/kotlin/jni/com/Action "); Construct_Id = env->GetMethodID(CLSS, "<init>", "()V "); Obj = env->NewObject(CLSS, construct_Id); Env ->GetMethodID(CLSS, "Action ", "()V"); Env ->CallVoidMethod(obj,method_Id); / / call the action () Method of the 2019-04-27 17:42:31. 774, 11880-11880 / com. Alex. Kotlin. Jni E / = = = = = = = : Method from the action in JavaCopy the code
  • To access a Java static instance, do the following:
  1. Find the class of the class
  2. Get fieldId based on the attribute name and the attribute’s data type
  3. Access properties according to fieldId
public class Action {
   private static int number = 100;
   public  int getNumber(a) {
     return number;
  }
  public  void setNumber(int number) { Action.number = number; }}Copy the code
  1. Declare native methods for accessing variables
public native void getStaticInstance(a);
Copy the code
  1. Access static variables in Jni methods
CLS = env - > FindClass (" alex/kotlin/jni/com/Action "); Field_id = env->GetStaticFieldID(CLS, "number", "I "); Id = env->GetStaticIntField(CLS, field_id); // Get the static variable jint num = 555 from the class; env->SetStaticIntField(cls, field_id, num); // Assign values to static variablesCopy the code
  1. Invoke and execute methods
Action action = new Action();
action.setMessage("Message in Java");
action.setNumber(123);

Log.e("Before======", action.getNumber() + "");
test.getStaticInstance();
Log.e("After Jni======", action.getNumber() + "");

2019-04-27 21:32:51.592 15022-15022/com.alex.kotlin.jni E/Before======: 123
2019-04-27 21:32:51.592 15022-15022/com.alex.kotlin.jni E/After Jni======: 555
Copy the code
  • Accessing a Java member instance (requires passing in an instance)
  1. Gets the class class based on the passed object
  2. Obtain fieldId based on the parameter name and parameter type
  3. Access properties based on fieldId and Object
public class Action {
   private String message = null;
   public String getMessage(a) {
      return message;
   }
   public void setMessage(String message) {
      this.message = message; }}Copy the code
  1. Declare native methods for accessing variables
public native void getInstance(Action action);
Copy the code
  1. Member variables are read in Jni
cls = env->GetObjectClass(obj); Class field_id = env->GetFieldID(CLS, "message", "Ljava/lang/String; ); STR = static_cast<jstring>(env->GetObjectField(obj, Field_Id)); New_str = env->NewStringUTF("Message in Jni "); Env ->SetObjectField(obj, field_id, new_str); // Set the value in ObjCopy the code
  1. Accessing a static instance
Log.e("Before======", action.getMessage() + "");
test.getInstance(action);
Log.e("After Jni======", action.getMessage() + "");

2019-04-27 21:32:51.592 15022-15022/com.alex.kotlin.jni E/Before======: Message in Java
2019-04-27 21:32:51.592 15022-15022/com.alex.kotlin.jni E/After Jni======: Message in Jni
Copy the code
  • Access constructors: From the example above, there are three steps to access constructors
  1. FindClass () finds the jclass of the class
  2. GetMethodID(CLSS, “”, “()V “) : Gets the constructor MethosId
  3. NewObject(CLSS, construct_Id) : Create an instance
  • Method to access the parent class
  1. FindClass () finds the jclass of the class
  2. GetMethodID(CLSS, “”, “()V “) : Gets the constructor MethosId
  3. NewObject(CLSS, construct_Id) : Create an instance of the class
  4. Use FindClas S to find the jClass object of the parent class
  5. Gets MethodId of the superclass method
  6. Call the superclass method, where you need to pass in both the subclass and the superclass object
public class Action { public void action(){ Log.e("==========","Action in Action"); } } public class ChildAction extends Action { @Override public void action() { Log.e("==========","Action in ChildAction"); }} CLS = env - > FindClass (" com. Alex. Kotlin. Jni. Action "); / / 1, jmethodID1 = env - > GetMethodID (CLS, "< init >", "() V"); //2, jobject1 = env->NewObject(CLS, jmethodID1); / / 3, cls_parent = env - > FindClass (" com. Alex. Kotlin. Jni. ChildAction "); //4, jmethodID2 = env->GetMethodID(cls_parent, "action()", "()V "); Env ->CallNonvirtualVoidMethod(jobject1, cls_parent, jmethodID2); / / 6,Copy the code

4. Reference types

  • Local reference
  1. Local references cannot be used across methods and threads and are equivalent to local variables in Java
  2. Prevents GC collection, and objects that are not referenced are automatically freed after the method ends and returned to Java, and prevented from collection if referenced
  3. All variables created in a method using the base method are local variables that can be released using DeleteLocalRef
  4. Resources must be released immediately after local variables are used up. Otherwise, an exception is thrown when the number of variables exceeds 512
  5. It is recommended to use Push/PopLocalFrame (to create a stack of references to local variables), use PushLocalFrame at the method entry, and call PopLocalFrame at each return, so that any variables created in the function will be released;
  • Global references
  1. Can be used across methods and threads, equivalent to member properties in Java
  2. It can only be created with the NewGlobalRef function
  3. This prevents the object from being recycled by GC, only by manually calling DeleteGlobalRef
  • Weak global reference
  1. Use NewGlobalWeakRef to create, use DeleteGlobalWeakRef to release
  2. It can be used across methods and threads
  3. GC collection is not prevented and will be collected when memory runs out, equivalent to if referenced in Java
  4. It is necessary to check whether the referenced object is reclaimed, so it is necessary to make a non-null judgment before each reference
  • Reference to compare
  1. IsSameObject(env, obj1, obj2) : Return JNI_TRUE (or 1) if obj1 and obj2 point to the same object, or JNI_FALSE (or 0) otherwise.
  2. Local and global references are NULL by comparing IsSameObject with NULL
  3. IsSameObject is used when weak global references are compared with NULL, and the return value is different from local references and global references to determine whether the object is reclaimed
  • Managing reference rules
  1. Do not increase global and weak references
  2. Do not omit any local references to the function trace
  3. When a method returns an instance, make it clear that it returns a global, local variable

5. Other knowledge

5.1, caching,
  • In-use caching
  1. Use static fields to cache data, initialize it after the first load, and then use the cached data directly
  2. The local reference cannot be cached. After the local reference is released, the null pointer may be cached
  • Statically initialized cache
  1. The native method initIDs is called directly when the library is imported
  2. Implement the initIds method in a C file, where the resource is cached
static { 
    System.loadLibrary("AccessCache"); 
    initIDs(); 
}
Copy the code
5.2 Jni exception handling
  • There is nothing in Jni like try… Catch () exception handling mechanism
  • In Jni, ThrowNew () is called to stop the program from executing immediately after throwing an exception
  • Methods for catching exceptions in Jni
  1. ExceptionCheck () : Checks whether Java throws an exception
  2. ExceptionDescribe () : Prints Java exception stack information
  3. ExceptionClear (1) : Clears abnormal stack information
  4. ThrowNew () : Manually throws a java.lang.exception Exception
//Java throws exception code
public void actionException(a) throws Exception {
   throw new Exception("Java throws an exception");
}
// call in C file
jmethodID1 = env->GetMethodID(cls, "actionException"."()V");
env->CallVoidMethod(obj, jmethodID1);
if (env->ExceptionCheck()) { // Check and throw an exception
    env->ExceptionDescribe();
    env->ExceptionClear();
    env->ThrowNew(env->FindClass("java/lang/Exception"), "Jni throws an exception");
}
// Run the result
2019-04-29 19:12:54.919 32350-32350/com.alex.kotlin.jni W/ system. err: java.lang.Exception: Java throws an Exception2019-04-29 19:12:54.920 32350-32350/com.alex.kotlin.jni E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.alex.kotlin.jni, PID: 32350java.lang.RuntimeException: Unable to start activity ComponentInfo{com.alex.kotlin.jni/com.alex.kotlin.jni.MainActivity}: java.lang.Exception: Jni throws an exceptionCopy the code
  • Catch exceptions with ExceptionOccurred
env->CallVoidMethod(obj, jmethodID1);
if (env->ExceptionOccurred()) {
    env->ExceptionDescribe();
    env->ExceptionClear();
    env->ThrowNew(env->FindClass("java/lang/Exception"), "Jni throws an exception");
}
Copy the code
  • Release resources after an exception occurs
 if ((*env)->ExceptionCheck(env)) { /* Exception check */
         (*env)->ReleaseStringChars(env, jstr, cstr); // Release the previously allocated memory after an exception occurs
         return; 
     }
Copy the code

6. Use Jni to generate So library files

For our normal development, the direct use of Jni scenarios are very few, general Jni methods will be encapsulated in the So library for the Java layer to call, now according to the above Jni knowledge using AS to generate a So library.

6.1 Android Studio Jni configuration

  • Download the NDK and build tools
  1. Select LLDB, CMake, and NDK from the SDK Manager of Android Studio
  • Configure the NDK – build
  1. Configure the NDK file path in the local.properties file
ndk.dir=/Users/wuliangliang/Library/Android/sdk/ndk-bundle
Copy the code
  • Creating a C++ project
  1. Create a new project select C++ project
  2. Once created, the project will first have a CPP folder with the Jni developed Demo configured (for reference)
  3. Create the JNI folder under the main folder
  • Configure cmakelist. TXT file, C code to generate so library configuration file
  1. Create text or copy cmakelist.txt in the CPP folder
cmake_minimum_required(VERSION 3.4.1) : configure the smallest version of the So library add_library(// Each C file has an add_library
        jni-lib  // Configure the So library name
        SHARED  // Set the So library to Shared library
        test.cpp)  // Relative path of source C file
find_library(  / /,
        log-lib
        log)
target_link_libraries(  
        jni-lib  // Specify the target library
        ${log-lib}) // Connect the target library to the NDK's log library
Copy the code

In the above configuration:

  1. Add_library () creates and names a So library. “jni-lib” is the name of the resulting So library, which is called “linjni-lib.so” file
  2. Test. CPP is the C code file corresponding to So library. The system will automatically locate and associate this file during compilation.
  3. Since NDK is already part of CMake search, you only need to set the name of the library to be used to NDK, use find_library () to locate the NDK in the configuration file, and store its path as a variable set. Use this variable to refer to NDK when building scripts later. The log-lib set here is the NDK variable
  4. To ensure that native libraries can call functions in the log, you need to associate libraries with the target_link_libraries () command
  • Gradle configuration: Associate the CMakeList configuration file path with the Jni file path
Android {externalNativeBuild {cmake {path "/ SRC/main/jni CMakeLists. TXT" / / configure CMakeList sourceSets file path}} {the main { Jni. srcDirs = [' SRC /main/jni', 'SRC /main/jni/']}}Copy the code

6.2. Create Native code, generate and use So library

  • Create a Java file and declare native methods
Public class JniTest {static {system.loadLibrary ("jni-lib "); } public static native String test(); // configure native methods}Copy the code
  • Generate the compiled.h file in the Jni folder
  • Create CPP files to implement native methods
JNIEXPORT void JNICALL Java_com_alex_kotlin_jni_JniTest_test
        (JNIEnv *env, jobject) {
    jclass cls = NULL;
    jmethodID method_Id = NULL;
    cls = env->FindClass("com/alex/kotlin/jni/Action");
    if (cls == NULL) {
        return;
    }
    method_Id = env->GetStaticMethodID(cls, "test"."()V");
    env->CallStaticVoidMethod(cls, method_Id);
    env->DeleteGlobalRef(cls);
}
Copy the code

Call FindClass () to get the Class object based on the Class name, use a global variable to save the Class object, and find and call actionStatic ()

  • TXT according to the name of the so library and run Make Project, the system will automatically create the so library in the build folder
  • Calling native methods
JniTest test = new JniTest();
tv.setText(test.test());
Copy the code

The basic knowledge and usage of Jni is basically introduced. It may not be used much in general development, but it is often used when you want to do further functions or optimization, so Jni has become the essential basic knowledge of Android advanced development. I hope the summary of this article will be helpful to students in need.