概述
当执行java的native方法时,虚拟机怎么知道要调用so中那个方法呢?这个就需要注册,通过注册把java的方法和so的方法绑定在一起,这样就可以找到对应的方法了,此篇文章仅作为笔记,以防以后忘记
有俩种注册的方式即 静态注册
和动态注册
静态注册
我们之前自动生成的项目就是静态注册的,我们看下代码
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());
}
当第一次调用调用java的native方法时,虚拟机会搜索对应的native函数,如果存在就会建立一个关联,以后再次调用,这部分操作就会由虚拟机完成
对应的规则
Java+包名+类名+方法名
其中用下划线进行分割,包名也用下划线进行分割
这样做会有点缺点:
- 名字太长
- 第一次调用需要搜索,影响效率
动态注册
- 需要我们手动建立联系,增加了代码量但提高效率
- 允许自己定义函数名字
加载动态库
java层通过System.loadLibrary()
方法可以加载一个动态库,此时虚拟机就会调用jni库中的JNI_OnLoad()
函数
jint JNI_OnLoad(JavaVM* vm, void* reserved);
返回值代表,动态库需要的jni版本,如果虚拟机不能识别这个版本,那么就不可以加载这个动态库
目前的返回值有,JNI_VERSION_1_1, JNI_VERSION_1_2, JNI_VERSION_1_4, JNI_VERSION_1_6。
如果动态库没有提供 JNI_OnLoad()
函数会默认使用JNI_VERSION_1_1
版本,但是这个版本太老,很多新函数没有,最好返回版本
注册函数
JNI_OnLoad()
函数经常用来做一些初始化操作,动态注册就是在这里进行的
动态注册通过_JNIEnv
的RegisterNatives()
函数来完成
函数原型为
jint RegisterNatives(JNIEnv *env, jclass clazz,
const JNINativeMethod *methods, jint nMethods);
- 第一个参数:JNIEnv *指针
- 第二个参数:代表一个java类
- 第三个参数:代表
JNINativeMethod
结构体数组,JNINativeMethod
定义了java层函数和native层函数的映射关系 - 第四个参数:代表第三个参数methods数组的大小
返回值 0代表成功,负值代表失败
JNINativeMethod结构体
RegisterNatives
函数最重要的就是JNINativeMethod
结构体,我们看下这个结构体
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
- name:代表java的native方法的名字
- signature:表示方法的签名
- fnPtr:是一个函数指针,指向jni层的一个函数,也就是java层和native层建立联系的函数
实践
现在我们有一个java类,里面有俩个native方法,下面我们来完成这俩个方法的动态注册
public class TextJni {
static {
System.loadLibrary("textjni_lib");
}
native int text(String message);
static native int static_text(String message);
}
首先手动书写C的对应的方法
jint native_text(JNIEnv *env, jobject jobject1, jstring msg)
jint native_staic_text(JNIEnv *env, jobject jclass1, jstring meg)
native_text
对应java层的text
方法,native_staic_text
对应java层的static_text
方法
那么是如何写出来的的呢?
- native方法名,这个可以随便起
- 返回值和java中的对应,java中是int,jni就是jint
- 第一个参数是固定的是JNIEnv指针
- 第二个参数目前我理解为固定jobject
- 后面参数就是java的参数转化过来的
然后需要填充JNINativeMethod
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;
//找到native方法所在的class
jclass class_text = env->FindClass("com.text.ndk1.TextJni");
if (env->RegisterNatives(class_text, nativeMethod,
sizeof(nativeMethod) / sizeof(nativeMethod[0])) == JNI_OK) {
result = 0;
}
return result;
}
这样就动态注册成功
看下完整代码
java代码
public class TextJni {
static {
System.loadLibrary("textjni_lib");
}
native int text(String message);
static native int static_text(String message);
}
C代码
#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.text.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;
}
}
调用
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("我是普通方法,哈哈");
}
}
看下打印信息
mmm: method = native_staic_text, msg = 我是静态方法,哈哈
mmm: method = native_text, msg = 我是普通方法,哈哈