preface
Native methods are often used in Android Studio projects. One of the methods is to introduce so files into the LIBS folder, so classes that need to use Native methods can directly call the following methods to load the library:
System.loadLibrary("test-lib");
Copy the code
It is possible to implement native methods directly in AS if you do not want to reference so files. In this case, you need to add the CPP file to the project and perform related configurations, as described in the following sections.
An overview of the
Environment to prepare
To compile CPP files in AS, you need to download the NDK and install the cmake tool, otherwise it won’t work.
NDK and cmake are easy to install, and can be handled directly in AS.
Tools->SDK Manager->Android SDK->SDK Tools
Copy the code
Click above to find SDK Tools, you can see a series of Tools for installation, the picture below is installed NDK and Cmake.
Create CPP directories and CPP files
Create the CPP folder in the following directory:
mkdir app/src/main/cpp/
Copy the code
Then create a CPP file under that directory:
Here name the CPP file test.cpp and add the following:
#include <jni.h> #include <string.h> JNIEXPORT jstring JNICALL Java_com_xxx_yyy_MainActivity_startTestJni( JNIEnv *env, jobject thiz) { char *hello = "hello test..." ; return env->NewStringUTF(hello); }Copy the code
Com_xxx_yyy is the package name and MainActivity is the native method used in the MainActivity class.
Each class corresponds to a native file, which has a one-to-one correspondence relationship. Attention should be paid here. Otherwise, the class cannot be found at runtime.
Create the cmakelists.txt file
Cmakelists. TXT is the rule for compiling CPP files, in which the name of the so library from which CPP files are compiled is specified. Create it as follows:
Name it cmakelists.txt. Add the following to the file:
Cmake_minimum_required (VERSION 3.4.1) add_library(# Sets the name of the library. Test -lib # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). src/main/cpp/test.cpp ) find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log ) target_link_libraries( # Specifies the target library. test-lib # Links the target library to the log library # included in the NDK. ${log-lib} )Copy the code
Specify the so library name, properties (whether to set to a shared library or not), and the source file in add_library.
In the build. Gradle configuration
Open build.gradle and append the following to the Android /defaultConfig area:
externalNativeBuild {
cmake {
cppFlags ""
}
}
Copy the code
Append the following to the Android section:
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
Copy the code
The final configuration is shown below:
MainActivity uses this native method
First declare the corresponding native method. For example, the native method added in test. CPP is as follows:
Java_com_xxx_yyy_MainActivity_startTestJni
Copy the code
So we declare the following in MainActivity:
public native String startTestJni();
Copy the code
Before calling this method, we need to load the corresponding so library, named test-lib, as follows:
static {
System.loadLibrary("test-lib");
}
Copy the code
This allows you to use the native method.
How to simplify native method names
In the example above, the native method name is dreadfully long, and the method name is limited by the package name, as follows:
JNIEXPORT jstring JNICALL Java_com_xxx_yyy_MainActivity_startTestJni
Copy the code
Such a pile of method names is simply unacceptable.
Method names are so limited because JNI methods use statically registered methods. As smart as you are, you might think that if you want to use a simple, unrestricted method name, you need to use a dynamically registered method.
To use the dynamic registration method, you need to implement the JNI_OnLoad method in the CPP file:
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL; jint result = -1; if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) ! = JNI_OK) { return -1; } assert(env ! = NULL); if (! registerNatives(env)) { return -1; } result = JNI_VERSION_1_6; return result; }Copy the code
When we call the system. loadLibrary method to load the so library, the System will look for the corresponding JNI_OnLoad method. If it does not find the JNI_OnLoad method, the default is static registration method.
If the JNI_OnLoad method is found, but the corresponding method is not registered, the system will throw an exception.
Dynamic registration implementation
Dynamic registration method source code is as follows:
Static JNINativeMethod gMethods[] = {{"startTestJni", "()Ljava/lang/String;" ,(void*) native_startTestJni }, }; static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods) { jclass clazz; clazz = env->FindClass(className); if (clazz == NULL) { return JNI_FALSE; } if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) { return JNI_FALSE; } return JNI_TRUE; } static int registerNatives(JNIEnv* env) {// Specify the class name of the native method, Const char* kClassName = "com/ XXX /yyy/MainActivity"; const char* kClassName = "com/ XXX /yyy/MainActivity"; return registerNativeMethods(env, kClassName, gMethods, sizeof(gMethods) / sizeof(gMethods[0])); }Copy the code
The registerNatives method is eventually called in the JNI_OnLoad method.
Here are the rules of gMethods:
static JNINativeMethod gMethods[] = { { "startTestJni", "()Ljava/lang/String;" ,(void*) native_startTestJni }, };Copy the code
StartTestJni is the Native method name defined in the Java class.
()Ljava/lang/String; Is the signature of the method, () means that the method has no parameters, Ljava/lang/String; Indicates that the return value is a String in Java. Other types of use and how to write with parameters, you can find relevant knowledge on the Internet, there are many.
(void*) native_startTestJni is the method name of the Native implementation. This is cast to a function pointer.
So if dynamic registration is used, the final test. CPP file looks like this:
#include <stdlib.h> #include <stdio.h> #include <string.h> #include <jni.h> #include <android/log.h> static const char *TAG = "test"; #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args) #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args) #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args) jstring native_startTestJni(JNIEnv * env, jobject thiz) { char *hello = "hello test ..." ; return env->NewStringUTF(hello); } static JNINativeMethod gMethods[] = { { "startTestJni", "()Ljava/lang/String;" ,(void*) native_startTestJni }, }; static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods) { jclass clazz; clazz = env->FindClass(className); if (clazz == NULL) { return JNI_FALSE; } if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) { return JNI_FALSE; } return JNI_TRUE; } static int registerNatives(JNIEnv* env) { const char* kClassName = "com/cvte/irsensor/MainActivity"; return registerNativeMethods(env, kClassName, gMethods, sizeof(gMethods) / sizeof(gMethods[0])); } JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL; jint result = -1; if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) ! = JNI_OK) { return -1; } assert(env ! = NULL); if (! registerNatives(env)) { return -1; } result = JNI_VERSION_1_6; LOGD("this is JNI_OnLoad for test-lib.." ); return result; }Copy the code
conclusion
This time, we want to make a serial port service, which refers to a SO library developed by a colleague before, but there is something wrong with this library, resulting in serial port communication failure. So I decided to write one myself. A lot of information on the net, but also stepped on the pit a lot, in this record, so as not to step on pit again next time.
Wechat official account
I also wrote an article in the wechat public number, update more timely, interested in the following two-dimensional code can scan, or wechat search [Android system combat development], pay attention to surprise oh!