概述
当执行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.taobao.alinnkit.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
#include
#include
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;
}
}
复制代码
调用
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 = 我是普通方法,哈哈
复制代码
参考
juejin.im/post/5d1f16…
blog.csdn.net/afei__/arti…
www.jianshu.com/p/b71aeb4ed…
关于找一找教程网
本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。
本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。
[Android JNI和NDK学习(三):动态注册]http://www.zyiz.net/tech/detail-138406.html