android jni中的java调c的两种方法
来源:互联网 发布:camera软件下载 编辑:程序博客网 时间:2024/05/18 19:45
Andoird 中使用了一种不同传统Java JNI的方式来定义其native的函数。
也就是java虚拟机通过一种机制可以找到对应的C函数
这里就涉及到静态注册和动态注册jni函数的方法
一.这里先说动态注册的方法
JNI 允许你提供一个函数映射表,注册给Jave虚拟机,这样Jvm/native就可以用函数映射表来调用相应的函数,
就可以不必通过函数名来查找需要调用的函数了,这种机制效率比较高
Java与JNI通过JNINativeMethod的结构来建立联系,它在jni.h中被定义,其结构内容如下:
typedef struct {
const char* name; //第一个变量name是Java中函数的名字。
const char* signature; //第二个变量signature,用字符串是描述了函数的参数和返回值
void* fnPtr; //第三个变量fnPtr是函数指针,指向C函数
} JNINativeMethod
当java通过System.loadLibrary加载完JNI动态库后,紧接着会查找一个JNI_OnLoad的函数,如果有,就调用它,
而动态注册的工作就是在这里完成的。
JNI_OnLoad()函数
JNI_OnLoad()函数在VM执行System.loadLibrary(xxx)函数时被调用,它有两个重要的作用:
指定JNI版本:告诉VM该组件使用那一个JNI版本(若未提供JNI_OnLoad()函数,VM会默认该使用最老的JNI 1.1版),如果要使用新版本的JNI,
例如JNI 1.4版,则必须由JNI_OnLoad()函数返回常量JNI_VERSION_1_4(该常量定义在jni.h中) 来告知VM。
初始化设定,当VM执行到System.loadLibrary()函数时,会立即先呼叫JNI_OnLoad()方法,因此在该方法中进行各种资源的初始化操作最为恰当。
如:
//定义jni_java函数映射表
static JNINativeMethod gMethods[] = {
{"yyjfunc", "()Ljava/lang/String;", (void *)Java_com_yyj_test_classname_yyjfunc},
{"yyjfunca", "(Ljava/lang/String;)I", (void *))Java_com_yyj_test_classname_yyjfunca},
{"yyjfuncb", "()V", (void *))Java_com_yyj_test_classname_yyjfuncb)
};
// 注册本地方法
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv *env = NULL;
jint result = -1;
// 从虚拟机中获得JNIEnv,同时指定jni版本
if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_4) != JNI_OK)
return JNI_FALSE;
// 注册本地方法
//获取类名
jclass clazz = (*env)->FindClass(env, "com/yyj/test/classname");
if (clazz == NULL)
return JNI_FALSE;
//把获取到的类和方法一起注册到虚拟机中
if ((*env)->RegisterNatives(env, clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0)
return JNI_FALSE;
result = JNI_VERSION_1_4;
return result;
}
其中比较难以理解的是第二个参数,这个涉及到 JNINativeMethod::signature 描述字符串字符
下面是说明:
1)基本类型对应关系:
标识符 Jni 类型 C 类型
V void void
Z jboolean boolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S jshort short
2)基本类型数组:(则以 [ 开始,用两个字符表示)
标识串 Jni 类型 C 类型
[I jintArray int[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
[D jdoubleArray double[]
[J jlongArray long[]
[Z jbooleanArray boolean[]
3)类(class):(则以 L 开头,以 ; 结尾,中间是用 / 隔开的 包 及 类名)
标识串 Java 类型 Jni 类型
L包1/包n/类名; 类名 jobject
例子:
Ljava/net/Socket; Socket jobject
4)例外(String 类):
标识串 Java 类型 Jni 类型
Ljava/lang/String; String jstring
5)嵌套类(类位于另一个类之中,则用$作为类名间的分隔符)
标识串 Java 类型 Jni 类型
L包1/包n/类名$嵌套类名; 类名 jobject
本地动态库函数 不但可以传递 Java 的基本类型,还可以传递字符串,数组,以及自定义类
第二个字段:
signature:它是一种对函数返回值和参数的编码。这种编码叫做JNI字段描述符(JavaNative Interface FieldDescriptors)。
例子:
"()V" void func() JNIEXPORT void JNICALL java_com_yyj_test_func(JNIEnv * env,jobject thiz )
"(II)V" void func(int,int) JNIEXPORT void JNICALL java_com_yyj_test_func(JNIEnv * env,jobject thiz,jint,jint )
"(Ljava/lang/String;Ljava/lang/String;)V" void func(String,String) JNIEXPORT void JNICALL java_com_yyj_test_func(JNIEnv * env,jobject thiz,jstring ,jstring )
"(Ljava/lang/String;IFJ)F" float func(String,int,float,long) JNIEXPORT jfloat JNICALL java_com_yyj_test_func(JNIEnv * env,jobject thiz,jstring ,jint,jfloat,jlong )
那我们再看看上面注册的函数怎么写
{"yyjfunc", "()Ljava/lang/String;", (void *)Java_com_yyj_test_classname_yyjfunc},
{"yyjfunca", "(Ljava/lang/String;)I", (void *))Java_com_yyj_test_classname_yyjfunca},
{"yyjfuncb", "()V", (void *))Java_com_yyj_test_classname_yyjfuncb)
如:
/*
* Class: com_yyj_test_classname
* Method: yyjfunc
* Signature: "()Ljava/lang/String;"
*/
JNIEXPORT jstring Java_com_yyj_test_classname_yyjfunc(JNIEnv * env ,jobject thiz) //需要返回一个字符串,可以这么返回
{
char cres[512];
jstring res;
const char *upgrade_value;
memset(cres, '\0', 512);
sprintf(cres,"%s",UPFILENAME);
res = (*env)->NewStringUTF(env, cres);
return res;
}
/*
* Class: com_yyj_test_classname
* Method: yyjfunca
* Signature:"(Ljava/lang/String;)I"
JNIEXPORT jint Java_com_yyj_test_classname_yyjfunca(JNIEnv * env ,jobject thiz,jstring filepath ) //需要返回一个字符串,可以这么返回
{
const char *pfilepath;
pfilepath = (*env)->GetStringUTFChars(env,filepath,0);
return 0;
}
二.还有一种办法可以让java能调到c函数,那就是静态注册
形如:JNIEXPORT jstring JNICALL Java_com_yyj_test_classname_yyjfunc
这种方式是根据Java+packname+method来实现的,名称过长,需要JNI开始时去遍历查找,效率有点低
这种方法可以先写好java代码,然后编译,生成一个 class文件,最后通过javah来自动生成jni对应函数声明
javah -jni -classpath yourclasspath -d jni_path classname (eg. com.yyj.test.classname)
- android jni中的java调c的两种方法
- Android jni 使用C语言调用java中的log方法
- JNI中的C回调java中的方法
- java JNI的两种实现方法:javah vs JNI_OnLoad
- android JNI简单的java调C
- android JNI简单的C调java
- JAVA用JNI方法调用C代码实现HelloWorld(含windows及ubuntu两种操作系统环境下的操作)
- Android JNI中C调用Java方法
- Android JNI-c/c++调用java方法
- Android-本地方法C调用Java中的方法/NDK-JNI开发实例(六)
- android JNI 使用的两种形式 --自己实现c和调用第三方so库
- Android NDK JNI之HelloWorld两种方法汇总
- Android studio 简单方便的使用JNI调用c语言中的方法
- JNI之C语言调用Java中的方法
- JNI开发基础篇:C语言调用Java中的方法
- android jni 调用java的方法
- Android JNI 在C中调用Java(包括自定义的Java方法和Log)
- android jni c语言回调java
- 《编程导论(Java)·9.3.1回调·2》什么是好莱坞法则
- Swift - 开关按钮(UISwitch)的用法
- Sofia-SIP辅助文档八 - Sofia SIP用户代理库 - "sresolv" - 异步DNS解析
- sql 语句
- Sofia-SIP辅助文档九 - Sofia SIP用户代理库 - "ipt" - 工具模块
- android jni中的java调c的两种方法
- Android项目使用python分语言批量打包apk渠道包
- Sofia-SIP辅助文档十 - Sofia SIP用户代理库 - "nua" - 高层用户代理模块
- Python学习
- 1007. Maximum Subsequence Sum (25)
- http请求资源下载还是打开显示
- 给VMWare虚拟机瘦身
- Sofia-SIP辅助文档十一 - Sofia SIP用户代理库 - "nea" - SIP事件模块
- swift-5类型别名和布尔值