JavaSE JNI 动态注册本地方法(c语言实现native层)
2015-01-28 11:15
716 查看
转载请注明出处:/article/2895645.html
引言:
最近结合着 Android 源码研究了一下 JNI ,发现 Android 上的 JNI 本地方法绑定使用的不是通过函数名进行绑定的静态绑定,而是使用了不常见的动态绑定。
于是在 JavaSE 中动手实现了一下 JNI 本地方法动态绑定,在实现过程中或多或少的出现了一些问题,而网上搜索到的相关文章大多数只是对 Android 源码的一个摘要,而没有具体在 JavaSE 中的实现,故作此文以补此空白。
本文不涉及 JNI 基础,仅仅是对本地方法动态绑定的一个讲解,不明白 JNI 为何物的小白同学请先 Google 研究一下 JNI 再来~~
动态注册本地方法需要一下几个数据结构与方法(可自行在 jni.h 中查看):
[c] view
plaincopy
// 该结构表示一个Java本地方法与native层的c方法的映射
typedef struct {
char *name; // Java本地方法名称
char *signature; // Java本地方法签名
void *fnPtr; // c函数指针
} JNINativeMethod;
// 该方法在JVM加载完JNI动态库的紧接着被调用,所以可以在这个函数里进行动态注册
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved);
// 该方法便是用来进行动态注册的
// clazz:本地方法所在类
// methods:表明方法映射关系的结构体数组
// nMethods:上一个参数的数组大小
jint (JNICALL *RegisterNatives) (JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods);
首先看一下声明和调用本地方法的 Java 代码:
[java] view
plaincopy
public class test {
static {
// 加载JNI动态库
System.loadLibrary("test_native");
}
public static native String nativeFunc(String msg);
public static void main(String[] args) {
System.out.println(nativeFunc("Hello Native"));
}
}
在这个代码中声明了一个 native 方法 nativeFunc,该方法的作用是将传入的字符串加上"(Sovled)"并然后返回修改后的字符串。
然后看一下 JNI 动态链接库的代码(C语言实现):
[c] view
plaincopy
#include <stdio.h>
#include <string.h>
#include <jni.h>
#include "test.h"
jstring native_function(JNIEnv *env, jclass cls, jstring msg);
// 定义Java本地方法与JNI动态库中方法的映射关系
static JNINativeMethod gMethods[] = {
{"nativeFunc", "(Ljava/lang/String;)Ljava/lang/String;", (void *)native_function}
};
// 注册本地方法
static int register_native_methods(JNIEnv *env, const char *clsname, const JNINativeMethod *gMethods, int nums)
{
jclass clazz = (*env)->FindClass(env, clsname);
if (clazz == NULL)
return JNI_FALSE;
if ((*env)->RegisterNatives(env, clazz, gMethods, nums) < 0)
return JNI_FALSE;
return JNI_TRUE;
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv *env = NULL;
jint result = -1;
// 获得JNIEnv
if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_4) != JNI_OK)
return JNI_FALSE;
// 注册本地方法
if (register_native_methods(env, "test", gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_TRUE)
return JNI_FALSE;
result = JNI_VERSION_1_4;
return result;
}
// 自定义方法,将msg加上"(Solved)"并返回
jstring native_function(JNIEnv *env, jclass cls, jstring msg)
{
const char *cmsg;
char cres[1024];
jstring res;
cmsg = (*env)->GetStringUTFChars(env, msg, JNI_FALSE);
memset(cres, '\0', 1024);
strcat(cres, cmsg);
strcat(cres, " (Solved)");
res = (*env)->NewStringUTF(env, cres);
(*env)->ReleaseStringUTFChars(env, msg, cmsg);
return res;
}
这里注意 JNI_OnLoad 方法中的操作,在 Android 源码中,获取 JNIEnv 的代码是 vm->GetEnv((void**) &env, JNI_VERSION_1_4),如果在我们的程序中这样写是错误的,会出现 request for member ‘GetEnv’ in something not a structure or union (或 在非结构或联合中请求成员‘GetEnv’)错误,这是因为 Android 源码中的 native 层的实现使用的是 C++,而我们这里使用的是
C语言,这也是网上搜到的很多文章出现的一个问题
最后便是对代码的编译和运行:
1. 编译生成动态链接库 libtest_native.so:
gcc -fPIC -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -shared -o libtest_native.so test.c
这里的 gcc 选项 -fPIC -shared 是用来生成动态链接库的,PIC选项的作用是生成 Position Independent Code,即生成不依赖与特定地址的机器代码,详细请看:http://stackoverflow.com/questions/5311515/gcc-fpic-option
2. 编译 Java 代码并运行
javac test.java
java -Djava.library.path=. test
注意:这里运行 Java 程序时需要加上 -Djava.library.path=. 来指定库加载位置,否则 Java 程序找不到之前生成的动态链接库。
引言:
最近结合着 Android 源码研究了一下 JNI ,发现 Android 上的 JNI 本地方法绑定使用的不是通过函数名进行绑定的静态绑定,而是使用了不常见的动态绑定。
于是在 JavaSE 中动手实现了一下 JNI 本地方法动态绑定,在实现过程中或多或少的出现了一些问题,而网上搜索到的相关文章大多数只是对 Android 源码的一个摘要,而没有具体在 JavaSE 中的实现,故作此文以补此空白。
本文不涉及 JNI 基础,仅仅是对本地方法动态绑定的一个讲解,不明白 JNI 为何物的小白同学请先 Google 研究一下 JNI 再来~~
动态注册本地方法需要一下几个数据结构与方法(可自行在 jni.h 中查看):
[c] view
plaincopy
// 该结构表示一个Java本地方法与native层的c方法的映射
typedef struct {
char *name; // Java本地方法名称
char *signature; // Java本地方法签名
void *fnPtr; // c函数指针
} JNINativeMethod;
// 该方法在JVM加载完JNI动态库的紧接着被调用,所以可以在这个函数里进行动态注册
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved);
// 该方法便是用来进行动态注册的
// clazz:本地方法所在类
// methods:表明方法映射关系的结构体数组
// nMethods:上一个参数的数组大小
jint (JNICALL *RegisterNatives) (JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods);
首先看一下声明和调用本地方法的 Java 代码:
[java] view
plaincopy
public class test {
static {
// 加载JNI动态库
System.loadLibrary("test_native");
}
public static native String nativeFunc(String msg);
public static void main(String[] args) {
System.out.println(nativeFunc("Hello Native"));
}
}
在这个代码中声明了一个 native 方法 nativeFunc,该方法的作用是将传入的字符串加上"(Sovled)"并然后返回修改后的字符串。
然后看一下 JNI 动态链接库的代码(C语言实现):
[c] view
plaincopy
#include <stdio.h>
#include <string.h>
#include <jni.h>
#include "test.h"
jstring native_function(JNIEnv *env, jclass cls, jstring msg);
// 定义Java本地方法与JNI动态库中方法的映射关系
static JNINativeMethod gMethods[] = {
{"nativeFunc", "(Ljava/lang/String;)Ljava/lang/String;", (void *)native_function}
};
// 注册本地方法
static int register_native_methods(JNIEnv *env, const char *clsname, const JNINativeMethod *gMethods, int nums)
{
jclass clazz = (*env)->FindClass(env, clsname);
if (clazz == NULL)
return JNI_FALSE;
if ((*env)->RegisterNatives(env, clazz, gMethods, nums) < 0)
return JNI_FALSE;
return JNI_TRUE;
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv *env = NULL;
jint result = -1;
// 获得JNIEnv
if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_4) != JNI_OK)
return JNI_FALSE;
// 注册本地方法
if (register_native_methods(env, "test", gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_TRUE)
return JNI_FALSE;
result = JNI_VERSION_1_4;
return result;
}
// 自定义方法,将msg加上"(Solved)"并返回
jstring native_function(JNIEnv *env, jclass cls, jstring msg)
{
const char *cmsg;
char cres[1024];
jstring res;
cmsg = (*env)->GetStringUTFChars(env, msg, JNI_FALSE);
memset(cres, '\0', 1024);
strcat(cres, cmsg);
strcat(cres, " (Solved)");
res = (*env)->NewStringUTF(env, cres);
(*env)->ReleaseStringUTFChars(env, msg, cmsg);
return res;
}
这里注意 JNI_OnLoad 方法中的操作,在 Android 源码中,获取 JNIEnv 的代码是 vm->GetEnv((void**) &env, JNI_VERSION_1_4),如果在我们的程序中这样写是错误的,会出现 request for member ‘GetEnv’ in something not a structure or union (或 在非结构或联合中请求成员‘GetEnv’)错误,这是因为 Android 源码中的 native 层的实现使用的是 C++,而我们这里使用的是
C语言,这也是网上搜到的很多文章出现的一个问题
最后便是对代码的编译和运行:
1. 编译生成动态链接库 libtest_native.so:
gcc -fPIC -I $JAVA_HOME/include -I $JAVA_HOME/include/linux -shared -o libtest_native.so test.c
这里的 gcc 选项 -fPIC -shared 是用来生成动态链接库的,PIC选项的作用是生成 Position Independent Code,即生成不依赖与特定地址的机器代码,详细请看:http://stackoverflow.com/questions/5311515/gcc-fpic-option
2. 编译 Java 代码并运行
javac test.java
java -Djava.library.path=. test
注意:这里运行 Java 程序时需要加上 -Djava.library.path=. 来指定库加载位置,否则 Java 程序找不到之前生成的动态链接库。
相关文章推荐
- JavaSE JNI 动态注册本地方法(c语言实现native层)
- JavaSE JNI 动态注册本地方法(c语言实现native层)
- Android JNI开发(1)--JavaVM和 JNIEnv 动态注册本地方法
- 使用JNI_OnLoad()函数实现本地方法的注册
- Android Studio NDK 入门教程(8)--JNI动态注册本地方法
- Android JNI静态和动态注册 、Java Reflect(C或C++层反射和JAVA层反射)、Java 可变参数(JNI实现)
- Java之本地整合方法----JNI的简略实现
- 在Windows中实现Java本地方法(JNI) - JMeteor????S Blog - CSDNBlog
- Java本地方法理解及通过JNI的简单实现
- java本地方法调用(JNI)的参考代码----实现将String转换成char*,将char*转换成String.
- Android JNI动态注册Native 方法(实现IDA中改名)
- java 本地方法的实现过程 JNI
- 使用jni实现在C语言中调用Java的方法
- Java通过JNI调用C语言的方法
- JAVA通过JNI调用本地C语言方法
- 实践Java中,Jni调用DLL文件本地方法
- JAVA通过JNI调用本地C语言方法
- JNI学习笔记3——本地方法取得Java属性/调用java方法
- Java调用C(Linux下实现Java本地方法)
- 用Java JNI 本地接口技术实现 圆形窗口