Jni中还可以采用RegisterNatives来注册jni的方法,注册以后的jni函数的命名可以不需要符合类似javah命令生成的函数的规则

RegisterNatives为JNIEnv的成员函数,声明为:
jint (JNICALL *RegisterNatives) (JNIEnv *env, jclass clazz, const JNINativeMethod *methods,jint nMethods);

其对应的取消注册的函数为声明为:
jint (JNICALL *UnregisterNatives) (JNIEnv *env, jclass clazz);

在java中调用System.loadLibrary("somelib");的时候,系统会自动调用jni的函数JNI_OnLoad,
在程序退出的时候,系统卸载“somelib”,会自动调用jni的函数JNI_OnUnload,
所以我们需要在jni的接口文件中重写这两个函数

以上一篇建立的HelloJni的例子来说明:
先定义一个字符串,内容为类名const char* JNIT_CLASS = "com/example/hellojni/HelloJni";

再定义一个函数的说明的数组

static JNINativeMethod gMethods[] = {
{"stringFromJNI", "()Ljava/lang/String;",(void*)stringFromJNI},
};

JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *jvm, void *reserved)
{
JNIEnv *env = NULL;
if ((*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_4)){
return JNI_ERR;
}

jclass cls = (*env)->FindClass(env, JNIT_CLASS);
if (cls == NULL)
{
return JNI_ERR;
}
jint nRes = (*env)->RegisterNatives(env, cls, gMethods, sizeof(gMethods)/sizeof(gMethods[0]));
if (nRes < 0)
{
return JNI_ERR;
}
return JNI_VERSION_1_4;
}

JNIEXPORT void JNICALL
JNI_OnUnLoad(JavaVM *jvm, void *reserved)
{
JNIEnv *env = NULL;
if ((*jvm)->GetEnv(jvm, (void**)&env, JNI_VERSION_1_4)){
return;
}
jclass cls = (*env)->FindClass(env, JNIT_CLASS);
if (cls == NULL)
{
return;
}
jint nRes = (*env)->UnregisterNatives(env, cls);
return;
}

其中JNIT_CLASS 表示的是要调用jni的java类的名称
gMethods[]为RegisterNatives的第三个函数,表示的是所有jni的函数的集合。


JNINativeMethod是表示jni方法的结构体,其结构如下:
typedef struct {

char *name;
char *signature;
void *fnPtr;

} JNINativeMethod;

第一个变量name为java类中native函数的名称,
第二个变量signature为java类中native函数的签名(由返回类型和参数类型决定)
第三个变量fnPtr为jni中对应的函数指针,格式为类似(void*)MethodName.

函数和变量的j签名可以通过命令 javap -s -p classname来获得,classname与使用javah时写的名称一致,javah生成的头文件的每个函数的注释中也有这个描述

对于“()Ljava/lang/String;”一个描述,表示该函数没有参数,返回值为String。括号内的是参数列表,后面跟的是返回值

java中简单类型和jni中的描述的对应关系如下表所示:

Field Descriptor Java Language Type
Z boolean
B byte
C char
S short
I int
J long
F float
D double

对于复杂类型,字符串描述以“L”开头,以“;”结束,例如java中的 String ,在jni中的描述为"Ljava/lang/String;"

对于数组,以“[”开头,接类型描述,例如int[ ],在jni中的描述为“[I”;String[ ], 对应为“[Ljava/lang/String;”;如果是数组维数增加一维,则"["增加一个,例如int[ ][ ],对应为“[[I”;

以上内容具体参见《The Java™ Native Interface Programmer’s Guide and Specification》一书,JNI中使用的各类型参见第12章 JNI TYPE


在使用JNI_OnLoad之时,我们不能把classname传递给jni,所以const char* JNIT_CLASS 是一个固定的名称,我们在生成SO文件的同时这个classname就固定下来了,所以,当提供so文件的同时,需要提供一个调用该so文件的java文件,类似c++中提供dll时需要提供的.h文件一样。

更多相关文章

  1. Android(安卓)初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  2. 深入理解SharedPrefences实现原理
  3. Android(安卓)?Pixelflinger 研究
  4. Android(安卓)权限中文描述大全
  5. 如何移植library到Android
  6. android 中FragmentActivity中模拟返回键返回上一个Activity效果
  7. 【从源码看Android】01从Looper说起
  8. 【学习笔记】Android中Service通信
  9. 最新Android面试题整理 5月

随机推荐

  1. Android的Monkeyrunner
  2. Android(安卓)源码学习资源
  3. (展讯 /RK3066)Android:ADB详解 (2013-01
  4. android 中几种adapter的总结
  5. Android(安卓)创建和使用公共library工程
  6. 从源码的角度分析Android中的Handler机制
  7. Android将Library上传到jcenter超简单完
  8. 第一个Android实例——计算器
  9. Android消息模型
  10. 如何在电脑上安装模拟器体验Android 4.0