Overview
When the native method of java is executed, how does the virtual machine know which method to call so? This requires registration. By registering, you can bind the java method and the so method together, so that you can find the corresponding method. This article is only a note, in case you forget it in the future
There are two kinds of ways that is registered 静态注册
and动态注册
Static registration
The project that we automatically generated before is statically registered, let's look at the code
extern "C" JNIEXPORT jstring JNICALL
Java_com_text_ndk1_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
When the native method of java is called for the first time, the virtual machine searches for the corresponding native function, if it exists, an association is established, and when it is called again later, this part of the operation will be completed by the virtual machine
Corresponding rules
Java+package name+class name+method name
Which is divided by underscores, and package names are also divided by underscores
This will have some disadvantages:
- Name is too long
- The first call needs to search, which affects efficiency
Dynamic registration
- We need to establish contact manually, which increases the amount of code but improves efficiency
- Allow you to define function names yourself
Load dynamic library
The java layer System.loadLibrary()
can load a dynamic library through methods, at which time the virtual machine will call the JNI_OnLoad()
functions in the jni library
jint JNI_OnLoad(JavaVM* vm, void* reserved);
The return value represents the jni version required by the dynamic library. If the virtual machine does not recognize this version, then the dynamic library cannot be loaded
The current return values are,JNI_VERSION_1_1, JNI_VERSION_1_2, JNI_VERSION_1_4, JNI_VERSION_1_6。
If the library does not provide dynamic JNI_OnLoad()
function will use the default JNI_VERSION_1_1
version, but this version is too old, not a lot of new functions, the best return version
Register function
JNI_OnLoad()
Functions are often used to do some initialization operations, and dynamic registration is performed here
Dynamic registration is completed by _JNIEnv
the RegisterNatives()
function
The function prototype is
jint RegisterNatives(JNIEnv *env, jclass clazz,
const JNINativeMethod *methods, jint nMethods);
- The first parameter: JNIEnv * pointer
- The second parameter: represents a java class
- The third parameter: represents the
JNINativeMethod
structure array, whichJNINativeMethod
defines the mapping relationship between java layer functions and native layer functions - The fourth parameter: represents the size of the third parameter methods array
Return value 0 means success, negative value means failure
JNINativeMethod structure
RegisterNatives
The most important thing about the function is the JNINativeMethod
structure. Let's take a look at this structure.
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
- name: represents the name of the native method of java
- signature: the signature of the method
- fnPtr: is a function pointer that points to a function of the jni layer, that is, the function that establishes a connection between the java layer and the native layer
practice
Now we have a java class with two native methods. Let’s complete the dynamic registration of these two methods.
public class TextJni {
static {
System.loadLibrary("textjni_lib");
}
native int text(String message);
static native int static_text(String message);
}
First, manually write the corresponding method of C
jint native_text(JNIEnv *env, jobject jobject1, jstring msg)
jint native_staic_text(JNIEnv *env, jobject jclass1, jstring meg)
native_text
The text
method native_staic_text
corresponding to the java layer, the static_text
method corresponding to the java layer
So how is it written?
- Native method name, this can be started at will
- The return value corresponds to that in java, int in java, jni is jint
- The first parameter is fixed is the JNIEnv pointer
- The second parameter is currently understood as a fixed jobject
- The following parameters are converted from java parameters
Then need to fillJNINativeMethod
static const JNINativeMethod nativeMethod[] = {
{
"text", "(Ljava/lang/String;)I", (void *) native_text},
{
"static_text", "(Ljava/lang/String;)I", (void *) native_staic_text}
};
I have already talked about this above
Then start registration
static int registNativeMethod(JNIEnv *env) {
int result = -1;
//找到native方法所在的class
jclass class_text = env->FindClass("com.taobao.alinnkit.ndk1.TextJni");
if (env->RegisterNatives(class_text, nativeMethod,
sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
result = 0;
}
return result;
}
Then the dynamic registration is successful
Look at the complete code
java code
public class TextJni {
static {
System.loadLibrary("textjni_lib");
}
native int text(String message);
static native int static_text(String message);
}
C code
#include <jni.h>
#include <string>
#include <android/log.h>
jint native_text(JNIEnv *env, jobject jobject1, jstring msg) {
const char *p_msg = env->GetStringUTFChars(msg, JNI_FALSE);
__android_log_print(ANDROID_LOG_INFO, "mmm", "method = %s, msg = %s", __FUNCTION__, p_msg);
return 0;
}
jint native_staic_text(JNIEnv *env, jobject jclass1, jstring meg) {
const char *p_msg = env->GetStringUTFChars(meg, JNI_FALSE);
__android_log_print(ANDROID_LOG_INFO, "mmm", "method = %s, msg = %s", __FUNCTION__, p_msg);
return 0;
}
static const JNINativeMethod nativeMethod[] = {
{
"text", "(Ljava/lang/String;)I", (void *) native_text},
{
"static_text", "(Ljava/lang/String;)I", (void *) native_staic_text}
};
static int registNativeMethod(JNIEnv *env) {
int result = -1;
jclass class_text = env->FindClass("com.taobao.alinnkit.ndk1.TextJni");
if (env->RegisterNatives(class_text, nativeMethod,
sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
result = 0;
}
return result;
}
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
int result = -1;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_1) == JNI_OK) {
if (registNativeMethod(env) == JNI_OK) {
result = JNI_VERSION_1_6;
}
return result;
}
}
transfer
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextJni.static_text("我是静态方法,哈哈");
new TextJni().text("我是普通方法,哈哈");
}
}
Look at the print information
mmm: method = native_staic_text, msg = 我是静态方法,哈哈
mmm: method = native_text, msg = 我是普通方法,哈哈
reference
https://juejin.im/post/5d1f16f16fb9a07ee46382e1#heading-9
https://blog.csdn.net/afei__/article/details/81031965
https://www.jianshu.com/p/b71aeb4ed13d