JNI
1.1 introduction to JNI
Java Nativie interface
Java local interface, Jni is a feature of Java calling local language. Jni enables Java and native languages to call each other
- For example, java and c/c + + call each other
1.2 implementation steps
-
-
Declare Native methods in java
-
public native String stringFromJNI();
-
-
The javac command compiles the java source file in 1 to get the class file
-
The javah -jni command exports the JNI header (. h) file
-
Use the local code that java needs to interact with to implement the native method declared in java
-
extern "C" JNIEXPORT jstring JNICALL Java_com_example_ndktest_NdkManager_stringFromJNI(JNIEnv *env, jobject thiz) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); }
-
-
Compile native code into dynamic libraries
- Under windows dll, linux is So, MAC system is jnilib
-
Execute Java program through Java command to realize java calling local code
-
1.3 what can the native layer do through jni
- Create and update java objects (including array strings)
- Call java method
- Load class and get class information
1.3.1 operation on class
-
GetObjectClass gets the class file. Generally, it gets the class file of the user-defined type passed in the parameter
-
Findclass (full path of class) gets the class file. Generally, get the class file of the class that is not in the parameter list but needs to be used. The parameter is the path of the target class
-
GetMethodID get method name
-
GetFieldID get field
-
See the following example for details
-
extern "C" JNIEXPORT jobject JNICALL Java_com_example_ndktest_NdkManager_changName(JNIEnv *env, jobject thiz, jobject st, jstring name) { //Reflection java method //1 get the class file corresponding to java jclass stClass = env->GetObjectClass(st); // 2 find the method getName to call jmethodID setAge = env->GetMethodID(stClass, "setAge", "(I)V"); jmethodID printInfo = env->GetStaticMethodID(stClass, "printInfo", "(Ljava/lang/String;)V"); jmethodID getName = env->GetMethodID(stClass, "getName", "()Ljava/lang/String;"); //3 call getName jstring sName = static_cast<jstring> (env->CallObjectMethod(st, getName)); // Convert the return value jstring to c available char const char *c = env->GetStringUTFChars(sName, 0); LOGE("The obtained name is:%s", c); //Release local reference env->ReleaseStringUTFChars(sName, c); // Call setAge env->CallVoidMethod(st, setAge, 25); // Call static method jstring str = env->NewStringUTF("printInfo"); env->CallStaticVoidMethod(stClass, printInfo, str); //Release local reference env->DeleteLocalRef(str); //Reflection class properties //Reflection get attribute id jfieldID nameId = env->GetFieldID(stClass, "name", "Ljava/lang/String;"); jfieldID ageId = env->GetFieldID(stClass, "age", "I"); //Set property id // name env->SetObjectField(st, nameId, name); // age env->SetIntField(st, ageId, 30); //Get methodid by passing the method of parameter object jmethodID toString=env->GetMethodID(stClass,"toSting", "(Lcom/example/ndktest/Student;)V"); //create object //1 get the class first. We got the Student's class file above, so we won't get it again here //2 get construction method jmethodID constour= env->GetMethodID(stClass,"<init>", "(Ljava/lang/String;I)V"); //3 Create Object jobject st1=env->NewObject(stClass,constour,env->NewStringUTF("jesse"),20); // 4. Call the method whose parameter is Studnet env->CallVoidMethod(st,toString,st1); // 5 release local references env->DeleteLocalRef(st1); env->DeleteLocalRef(stClass); return st; }
-
1.3.2 get parameters passed from java layer
-
The parameter is an array
-
All reference type arrays are jobjectArray
-
java
-
// Reference data type is parameter public native int Test(int[] list, String[] str);
-
c++
-
extern "C" JNIEXPORT jint JNICALL Java_com_example_ndktest_NdkManager_Test(JNIEnv *env, jobject thiz, jintArray i_Array, jobjectArray str) { // Get the address of the first element of the array // Second parameter // jboolean* isCopy // true indicates that the newly requested memory copies a new array // false is to use java arrays jint *p = env->GetIntArrayElements(i_Array, NULL); // If it is a c environment, you need to replace env - > with (* Env) - > //jint *p1 =(*env).GetIntArrayElements(i_Array, NULL); // Because JNIEnv itself is a pointer in c environment, if it is declared as a pointer again here, it will become a secondary pointer. Therefore, it is necessary to solve a reference and get a primary pointer for operation //Gets the length of the array jint length = env->GetArrayLength(i_Array); for (int i = 0; i < length; i++) { if (i == 3) { *(p + i) = 10; } LOGE("Acquired java Array value is: %d", *(p + i)); } // Release array // Parameter 1 // Parameter 2 // Parameter 3: mode mode has three values // 0 refresh java array and release c/c + + array // 1 = JNI_COMMIT: refresh JAVA array only // 2 = JNI_ABORT: release only c/c + + arrays env->ReleaseIntArrayElements(i_Array, p, 0); //The type array is traversed in the following way. First, get the length of the array, and then press the subscript to get it int count = env->GetArrayLength(str); for (int i = 0; i < count; i++) { // Get jstring jstring s1 = static_cast<jstring>(env->GetObjectArrayElement(str, i)); // Can be converted to C / char in C + + const char *c = env->GetStringUTFChars(s1, 0); LOGE("Acquired java str Array value is: %s", c); // release env->ReleaseStringUTFChars(s1, c); } return count; }
-
-
-
The parameter is a basic data type
-
Can be used directly
-
java
-
// The basic data type is parameter public native int Test1(int i);
-
c++
-
extern "C" JNIEXPORT jint JNICALL Java_com_example_ndktest_NdkManager_Test1(JNIEnv *env, jobject thiz, jint i) { return i + 1; }
-
-
-
The parameter is a custom type
- You need to operate through the method of the operation class
- Please refer to the example in 1.3.1 above
1.4 JNI syntax
-
Declaring native methods in java
-
public native String stringFromJNI();
-
-
Implement the declared native method in native
-
extern "C" JNIEXPORT jstring JNICALL Java_com_example_ndktest_NdkManager_stringFromJNI(JNIEnv *env, jobject thiz) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); }
-
1.4.1 code interpretation in native
1.4.1.1 extern "C"
1.4.1.2 JNIEXPORT
Macro definition: #define jniexport__ attribute__ ((visibility ("default")) in UNIX systems such as Linux/Unix/Mac os/Android, it is defined as__ attribute__ ((visibility ("default")))
-
Indicates that the function is visible to the outside world
-
GCC has a visibility attribute, which means enabling this attribute:
- When - fvisibility=hidden, the functions in the dynamic library are hidden by default.
- When - fvisibility=default, the functions in the dynamic library are visible by default.
1.4.1.3 JNICALL
Macro definition: in UNIX systems such as Linux/Unix/Mac os/Android, it is an empty macro definition: #define jnical, so you can delete it on android
1.4.1.4 thiz
- thiz is the class that declares the native method in java, that is, the class that calls the native method
1.4.1.5JNIEnv
- JNIEnv type actually represents the Java environment. Through this JNIEnv * pointer, you can operate the code on the Java side:
- Call Java function
- Manipulating Java objects
- The essence of JNIEnv is a thread related structure. There is one JNIEnv for each thread
1.4.1.6 JavaVM
- JavaVM: JavaVM is the representative of Java virtual machine in JNI layer. There is only one in JNI global
- JNIEnv: the representative of JavaVM in threads. Each thread has one. There may be many jnienvs in JNI. At the same time, JNIEnv has thread correlation, that is, thread B cannot use the JNIEnv of thread A
1.4.1.7 how to use JNIenv in native threads
-
If you want to use JNIEnv * in the native thread, you need to bind with the AttachCurrentThread method of the JVM. When the child thread exits, you need to call the DetachCurrentThread function of JavaVM to release the corresponding resources, otherwise an error will occur.
-
JavaVM *_vm; jint JNI_OnLoad(JavaVM* vm, void* reserved){ _vm = vm; return JNI_VERSION_1_6; } void* threadTask(void* args){ JNIEnv *env; // Bind the current thread and assign value to env through javavm jint result = _vm->AttachCurrentThread(&env,0); if (result != JNI_OK){ return 0; } // ... // Don't forget to detach the thread after the task is executed _vm->DetachCurrentThread(); } extern "C" JNIEXPORT void JNICALL Java_com_example_ndktest_NdkManager_nativeThreadTest(JNIEnv *env, jobject thiz) { pthread_t pid; pthread_create(&pid,0,threadTask,0); }
-
Here through jni_ This method involves the registration method of jni, which will be described in detail later
-
1.5 JNI registration type
1.5.1 static registration
-
When the Java layer calls the navtie function, it will find the corresponding JNI function according to the function name in the JNI library. If it is not found, an error will be reported. If found, an association relationship will be established between the native function and the JNI function. In fact, it is to save the function pointer of the JNI function. Next time you call the native function, you can directly use this function pointer.
-
grammar
- JNI function name format (the "." in the package name needs to be changed to "")
- Java_ + Mainu function name (# jndmu package name) + #com
-
Disadvantages of static registration
- It is required that the name of JNI function must follow the naming format of JNI specification;
- The name is long and error prone;
- The first call will search the corresponding function in JNI according to the function name, which will affect the execution efficiency;
- All Java classes that declare native functions need to be compiled, and each generated class file needs to generate a header file with javah tool;
-
example
-
Package name: com example. ndktest
-
Class name NdkManager
-
java class
-
package com.example.ndktest; import java.util.ArrayList; import java.util.List; public class NdkManager { static { // native is implemented in the ndktest library, so you need to load the library, which will be mentioned later in cmake System.loadLibrary("ndktest"); } public native String stringFromJNI(); }
-
-
native implementation
-
#include <jni.h> #include <string> #include <android/log.h> // Define log macro output__ VA_ARGS__ Indicates that the variable parameter represents "..." in the preceding brackets #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "JNI", __VA_ARGS__); extern "C" JNIEXPORT jstring JNICALL Java_com_example_ndktest_NdkManager_stringFromJNI(JNIEnv *env, jobject thiz) { std::string hello = "Hello from C++"; // 1. Get the Class of thiz, that is, the Class information in java jclass thisclazz = env->GetObjectClass(thiz); // 2. Get the methodID of the getClass method according to the Class. The third parameter is the signature (params)return jmethodID mid_getClass = env->GetMethodID(thisclazz, "getClass", "()Ljava/lang/Class;"); // 3. Execute getClass method to obtain Class object jobject clazz_instance = env->CallObjectMethod(thiz, mid_getClass); // 4. Get Class instance jclass clazz = env->GetObjectClass(clazz_instance); // 5. According to the methodID of class jmethodID mid_getName = env->GetMethodID(clazz, "getName", "()Ljava/lang/String;"); // 6. Call getName method jstring name = static_cast<jstring>(env->CallObjectMethod(clazz_instance, mid_getName)); //Print thiz class name LOGE("class name:%s", env->GetStringUTFChars(name, 0)); return env->NewStringUTF(hello.c_str()); }
-
-
1.5.2 dynamic registration
-
Provide a function mapping table and register it with the JVM virtual machine, so that the JVM can call the corresponding function with the function mapping table, and there is no need to find the function to be called through the function name.
-
Java and JNI establish a function mapping table through the structure of JNI nativemethod, which is in JNI H is defined in the header file, and its structure is as follows:
-
typedef struct { const char* name; // Corresponding method name in java const char* signature; // Method signature void* fnPtr;// /Function pointer corresponding to the method in interactive cpp (pointing to the corresponding function) } JNINativeMethod;
-
-
After creating the mapping table, call the env->RegisterNatives function to register the mapping table to JVM.
- When the Java layer passes through system Loadlibrary when loading JNI library, JNI will be checked in the library_ Onload function.
- JNI_OnLoad is the entry function of JNI library. All function mapping, dynamic registration and other initialization work need to be completed here.
-
example
-
Java class name NativeManager package name com example. dynamicndk
-
public class NativeManager { // Used to load the 'dynamicndk' library on application startup. static { System.loadLibrary("dynamicndk"); } // Dynamic registration public native String stringFromJNI(); public native void Test(); // native thread calls java public native void threadTest(); }
-
-
cpp
-
#include <jni.h> #include <string> #include <android/log.h> #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "JNI", __VA_ARGS__); JavaVM *_vm;//Save the JavaVM declaration as a global variable, which is convenient for extracting JNIEnv or when needed char *mClassName = "com/example/dynamicndk/NativeManager"; void Test(JNIEnv *env, jobject thiz) { LOGE("Dynamic registration"); } jstring stringFromJNI(JNIEnv *env, jobject thiz) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); } // Create the mapping table corresponding to the native function to be bound JNINativeMethod jniMethod[] = { {"Test", "()V", (void *) Test}, {"stringFromJNI", "()Ljava/lang/String;", (jstring *) stringFromJNI}, }; // System. The first function to execute after loadlibrary int JNI_OnLoad(JavaVM *vm, void *unused) { LOGE("JniOnLoad"); //Save as global variable javaVM _vm = vm; // Save global variable JNIEnv JNIEnv *env = nullptr; //nullptr replaces NUll to assign a value to the pointer, or you can write 0 directly // Here, the address of the env pointer should be passed in to facilitate the assignment of the pointer pointed to by the address int success = vm->GetEnv((void **) &env, JNI_VERSION_1_6); //Judge success if (success != JNI_OK) { LOGE("SUCCESS %d:", success); return -1; } // Get the class to bind, that is, the corresponding java interaction class jclass jcls = env->FindClass(mClassName); // Register the native method to associate env->RegisterNatives(jcls, jniMethod, sizeof(jniMethod) / sizeof(JNINativeMethod)); return JNI_VERSION_1_6; }
-
-
-
Step summary
-
- Create function mapping table
- Get the java class corresponding to the function
- Get JNIEnv and register the function mapping table through JNIEnv
-
-
advantage
- More flexible dynamic registration and naming
1.6 data type conversion
1.6.1 basic data type
java type | Native type | describe |
---|---|---|
boolean | jboolean | unsigned 8 bits integer |
byte | jbyte | signed 8 bits integer |
char | jchar | unsigned 16 bits integer |
short | jshort | signed 16 bits integer |
int | jint | signed 32 bits integer |
long | jlong | signed 64 bits integer |
float | jfloat | signed 32 bits floating point |
double | jdouble | signed 64 bits floating point |
void | void | No shaping |
1.6.2 reference data type
java | native |
---|---|
object | jobject |
java.lang.Class instance | jclass |
java.lang.String instance | jstring |
array | jarray |
Object[] | jobjectArray |
boolean[] | jbooleanArray |
byte[] | jbyteArray |
char[] | jcharArray |
short[] | jshortArray |
int[] | jintArray |
long[] | jlongArray |
float[] | jfloatArray |
double[] | jdoubleArray |
java.lang.Throwable objects | jthrowable |
1.6.3 function signature
-
format
-
[parameter 1 type character parameter 2 type character...] Return value type character
-
Note that the reference type should start with L, followed by the full path of the type and start with; ending
- Example: Ljava/lang/String;
-
parameter
-
If it is multi parameter, there is no need to add type characters directly after the interval
-
example
-
java method Method signature String getStr(int a,int b) (II)Ljava/lang/String; String getS(int a,String b) (ILjava/lang/String;)Ljava/lang/String;
-
-
If there is no parameter, no content will be written in parentheses. If there is no return value, the return value position is V
- Example (V)
-
-
Note that the reference type should start with L, followed by the full path of the type and start with; ending
- Example: Ljava/lang/String;
-
Comparison table
-
Java type Corresponding character void V boolean Z int I long J double D float F byte B char C short S int[] [I (array starts with [followed by corresponding type character) String Ljava/lang/String; (reference type starts with L and ends with classpath) Object[] [Ljava/lang/object;
-
1.7 JNI references
Local Reference
-
Created inside the function, the declared variables and objects belong to local references
-
When the method call ends, the local reference is automatically released
-
Of course, it can also be released manually
-
DeleteLocalRef() // Delete is used to create objects ReleaseXXX
-
Global Reference
-
JNI allows you to create global variables from local variables
-
//Declare global references jclass st1Class; extern "C" JNIEXPORT jobject JNICALL Java_com_example_ndktest_NdkManager_changName(JNIEnv *env, jobject thiz, jobject st, jstring name) { //Find class if(st1Class==NULL){ jclass cls = env->FindClass("com/example/ndktest/Student"); //Declare as global reference st1Class= static_cast<jclass>(env->NewGlobalRef(cls)); env->DeleteLocalRef(cls);//Release local reference //env->DeleteGlobalRef(st1Class);// Release global reference } }
-
-
Can cross methods and threads
-
DeleteGlobalRef needs to be called to release
Weak Global Reference
-
Similar to global references, weak references can be used across methods and threads. Unlike global references, weak references do not prevent the GC from reclaiming objects inside the VM it points to
-
Therefore, when using weak references, you must first check whether the cached weak references point to the active object or to an object that has been GC
-
establish
-
//Declare global references jclass st1Class; extern "C" JNIEXPORT jobject JNICALL Java_com_example_ndktest_NdkManager_changName(JNIEnv *env, jobject thiz, jobject st, jstring name) { //Determine whether to point to the active object jboolean isEqual =env->IsSameObject(st1Class,NULL); if(st1Class==NULL||isEqual){ jclass cls = env->FindClass("com/example/ndktest/Student"); //Declare as global reference st1Class= static_cast<jclass>(env->NewWeakGlobalRef(cls)); env->DeleteLocalRef(cls);//Release local reference //env->DeleteWeakGlobalRef(st1Class);// Release weak global references } }
-
-
release
- Call DeleteWeakGlobalRef to release
NDK
brief introduction
Android NDK is a set of tools that allow you to embed C or C + + ("native code") into Android applications. NDK describes a tool set
It is the native code of c/c + + called by jni
List of important structures
- We can view the directory structure of ndk in SDK / ndk bundle. Here are three important members:
- NDK build: this Shell script is the starting point of Android NDK building system. Generally, only executing this command in the project can compile the corresponding dynamic link library.
- platforms: this directory contains header files and library files that support different Android target versions. The NDK construction system will reference header files and library files under the specified platform according to the specific configuration.
- toolchains: this directory contains cross compilers under different platforms supported by NDK - ARM, X86 and MIPS. At present, ARM is commonly used// todo ndk-depends.cmd
Cross compilation
- The process of compiling secondary files that can be executed on one platform is called cross compilation
- For example, compile the available library files of android on windows
Library file format
- Static library a
- When compiling the link, all the code of the library file is added to the executable file, so the generated file is relatively large, but the library file is no longer needed at runtime. The suffix in linux is " a”
- Dynamic library so
- When compiling the link, the code of the library file is not added to the executable file, but the library is loaded by the runtime link file when the program is executed. The suffix in linux is " so ", gcc uses the dynamic library by default when compiling.
makefile (.mk) compilation
brief introduction
Makefile is "automatic compilation": the source files in a project are not counted, and they are placed in several directories according to type, function and module. Makefile defines a series of rules to specify which files need to be compiled first, which files need to be compiled later, how to link and so on. Android uses Android MK file to configure makefile
1.1 Android.mk
-
# The location of the source file in. The macro function my dir returns the path of the current directory (the directory containing the Android.mk file itself). LOCAL_PATH := $(call my-dir) # Import other makefile files. CLEAR_ The vars variable points to the special GNU Makefile, which can clear many local files for you_ XXX variable # Local will not be cleared_ Path variable include $(CLEAR_VARS) # Specify the library name. If the beginning of the module name is lib, the construction system will not attach additional prefix lib; Instead, the module name is adopted as is and added so extension. LOCAL_MODULE := hello # Contains a list of C and / or C + + source files to build into the module, separated by spaces LOCAL_SRC_FILES := hello.c # Build dynamic library include $(BUILD_SHARED_LIBRARY)
1.2 settings corresponding to Gradle
-
app/gradle
-
apply plugin: 'com.android.application' android { compileSdkVersion 29 defaultConfig { ... // The source file should be compiled into several CPUs so externalNativeBuild{ ndkBuild{ abiFilters 'x86','armeabi-v7a' } } // Several kinds of so need to be packaged into apk ndk { abiFilters 'x86','armeabi-v7a' } } // Configure native build script location externalNativeBuild{ ndkBuild{ path "src/main/jni/Android.mk" } } // Specify ndk version ndkVersion "20.0.5594570" ... } dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) ... }
-
-
Google recommends developers to use cmake instead of makefile for cross compilation. When introducing a third-party precompiled so, there will be some differences before and after android 6.0. For example, before 6.0, you need to manually system Loadlibrary is a third-party so, which is not required later. There are many configuration parameters about Makefile, which are not explained here. For more reference Official documents.
- Below 6.0, system Loadlibrary will not automatically load so. The internal dependent so is below 6.0, system Loadlibrary will automatically load the so that is internally dependent on so, so version compatibility is required when using mk
Cmake compilation
- Is a build tool
1.1 CMakeLists.txt
-
# cmake version cmake_minimum_required(VERSION 3.10.2) #Declare and name items project("ndktest") # Declare and name Libraries # Set it to share or not. Here are three values # SHARED: represents a dynamic library, which can be used in (Java) code Loadlibrary (name) is called dynamically; # STATIC: represents a STATIC library, which will be called at compile time when integrated into the code; # MODULE: only valid in the system using dyId. If dyId is not supported, it will be treated as SHARED; # EXCLUDE_FROM_ALL: indicates that this library is not built by default, unless it is dependent on other components or built manually; # Add the path to the source file in the library add_library( # Set the name of the library. For example, ndktest will now be generated so ndktest # Set library as shared library SHARED # Provide relative path of source file native-lib.cpp) # Search and specify the pre built library and store the path as a variable (log Lib in this case). # There are already some pre built libraries (such as log) in the NDK, and the NDK library is already configured as part of the cmake search path # You can write directly in the target without writing_ link_ log Libraries # For example, we introduced Android / log H is liblog. In the ndk directory of the call The lib in front of so can be omitted and can be found by writing log directly find_library( # Set path variable name log-lib # Find the so library path with the specified name from the system path and assign it to the log lib above log) # Specifies the library that CMake should link to the target library. You can link multiple libraries, such as libraries defined in this build script, pre built third-party libraries, or system libraries. target_link_libraries( # Specify target library ndktest # The path to link the target library. Here is the path of log, that is, find above_ Log Lib in Library # This is a link to the target library path in the form of variables. Of course, we can also find in the province_ Library this step directly specifies the target library # For example, the ndktest here can also be found by writing log directly # If you need to introduce a third-party so, you have to specify a directory to find it ${log-lib})
1.2 configuration in gradle
-
android { //...... defaultConfig { applicationId "com.example.ndktest" minSdk 21 targetSdk 31 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" externalNativeBuild { cmake { // Setting the c + + standard cppFlags '-std=c++11' //You need to generate so under several cpu architectures. If you don't write it, it will be generated by default abiFilters "armeabi-v7a","x86" } } // Package apk packages supporting several architectures. For example, apks supporting x86 and armeabi-v7a architectures will be generated here ndk { abiFilters 'x86','armeabi-v7a' } } // Different packaged apk architectures splits { abi { enable true reset() include 'armeabi-v7a', 'x86' universalApk true } } //...... // Configure the file path of the native build script externalNativeBuild { cmake { path file('src/main/cpp/CMakeLists.txt') version '3.10.2' } } //..... }
-
externalNativeBuild compiles the guidance source file in defaultConfig and configures the build script path of native outside defaultConfig
Add multiple source files
Reference the third-party dynamic library so
-
If you reference a so - > libtest. Com written in c So contains only one hc file
-
Here cmakelist Txt is moved from the cpp directory to the app directory to facilitate the later splicing path
-
Put the so of the corresponding architecture under the corresponding architecture directory of jinLibs. Here, take armeabi-v7a as an example
Mode 1
-
Add cmake lookup path and directly add a lookup path to cmake, under which external can be found
-
In cmakelist Txt add the following code level and target_link_libraries,find_library of the same level
-
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_CURRENT_SOURCE_DIR}/jniLibs/${CMAKE_ANDROID_ARCH_ABI}")
-
Cmakelist Txt
- CMAKE_CXX_FLAGS is compiled into variable names in c + + environment using c + +
- CMAKE_C_FLAGS is compiled into variable names in the C environment using C
-
This variable is passed to the compiler
-
Redefine the variable and specify the target path through - L. you can dynamically identify the directory through the following variables to find the specified so
- CMAKE_SOURCE_DIR current file path
- ANDROID_ABI is the abi directory
-
Write test to target_link_libraries indicates that we want to link libtest so
-
When so starts with lib, it is written to target_link_libraries or find_ In library, lib can be omitted and only write the name
-
target_link_libraries( # Specifies the target library. ndktest test # Links the target library to the log library # included in the NDK. ${log-lib})
-
-
Mode II
-
In cmakelist Txt add the following code level and target_link_libraries,find_library of the same level
-
And the so needs to be load ed in systemload
-
This method can still be used below android 6.0, and the problem of wrong directory will occur above 6.0. Therefore, we generally use the first method in cmake
-
# test On behalf of third parties so - libtest.so# SHARED Represents dynamic library, and static library is STATIC;# IMPORTED: Indicates that it is added in the form of import(Precompiled Library)add_library(test SHARED IMPORTED)#set up test Import path for(IMPORTED_LOCATION) attribute,Relative paths cannot be used# CMAKE_SOURCE_DIR: current cmakelists.txt Path of( cmake Tools (built-in)# Built in droid Android_ ABI: the cpu architecture that needs to be compiled at present_ target_ properties(external PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libtest.so)
-
Write test to target_link_libraries indicates that we want to link libtest so
-
target_link_libraries( # Specifies the target library. ndktest test # Links the target library to the log library # included in the NDK. ${log-lib})
-
-
Use after introducing so
If libtest test() function exists in so
Condition 1 exists h
-
Direct #include < test h>
-
If the test function is written in c
-
Our environment is also a c + + environment, so we need to mark extern "C" in advance to call normally
-
extern "C" { extern void test();}
-
-
If it is not a c + + environment, you can call the test function directly
-
-
Case 2 does not exist h
-
The function should be declared as extern, that is, the external definition is referenced here
-
extern void test();
-
If the test function is written in c
-
Our environment is also a c + + environment, so we need to mark extern "C" in advance to call normally
-
extern "C" { extern void test();}
-
-
If it is not a c + + environment, you can call the test function directly
-
-
Introduce static library a
-
You don't have to put it in the jnilibs directory. You just need to set the search path of the library like referencing so
-
# test1 represents a third party The full name of a file is libtest1 a # SHARED Represents dynamic library, and static library is STATIC;# IMPORTED: indicates that it is added in the form of import (precompiled Library) add_library(test1 STATIC IMPORTED) #Set the imported_location attribute of test. Relative paths cannot be used # CMAKE_SOURCE_DIR: current cmakelists Txt path (cmake tool built-in) # android cmake built-in Android_ ABI: the cpu architecture AS3.0 that needs to be compiled at present After 2, ${ANDROID_ABI} is changed to ${CMAKE_ANDROID_ARCH_ABI} set_target_properties(external PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libtest1.a)
-
The usage is the same as the above so usage