java 本地方法调用(JNI)

int32位 posted @ Oct 02, 2013 09:13:11 PM in java , 3623 阅读
转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!

java使用native关键字声明本地方法,和声明通常的方法没有什么区别,只有两点不同:一是必须有native关键字,二是以分号结束,没有{},因为在类中并没有实现,这和声明抽象方法类似。本地方法一般是c/c++实现。下面以一个简单的例子来实现一个类,这个类调用本地方法。

public class JNIDemo {
	public native String input(String prompt);
	public native int sum(int a, int b);
	public native int sum(int[] a);
	static {
		System.load("/home/fgp/program/java/jni/libutil.so");

		/* 以下方法,需要设置lib库的路径 */
		//System.loadLibrary("util");

	}
	public static void main(String[] args) {
		JNIDemo jni = new JNIDemo();
		String name = jni.input("Type your name:");
		System.out.println("Your name is " + name);
		System.out.println(jni.sum(1, 2));
		System.out.println(jni.sum(new int[]{1, 2, 3, 4, 5}));
	}
}

我们声明了三个方法,同时使用了方法重载。这个三个方法均在main方法中被调用。

使用javac命令编译:

javac JNIDemo.java

此时如果运行java JNIDemo,则会出现异常,因为我们还没有实现本地方法。

在实现本地方法之前,最好先自动生成c头文件,使用javah:

javah -jni JNIDemo

此时会自动产生c头文件,

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JNIDemo */

#ifndef _Included_JNIDemo
#define _Included_JNIDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JNIDemo
 * Method:    input
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_JNIDemo_input
  (JNIEnv *, jobject, jstring);

/*
 * Class:     JNIDemo
 * Method:    sum
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_JNIDemo_sum__II
  (JNIEnv *, jobject, jint, jint);

/*
 * Class:     JNIDemo
 * Method:    sum
 * Signature: ([I)I
 */
JNIEXPORT jint JNICALL Java_JNIDemo_sum___3I
  (JNIEnv *, jobject, jintArray);

#ifdef __cplusplus
}
#endif
#endif

初次看起来与我们平常的头文件有点不同,这就是java与本地方法通信的协议,或者说规范。比如函数名必须是Java_ClassName_MethodName__Sigature(JNIEnv *, jobject, arg...)。Sigature主要解决c没有实现重载的问题。函数形参JNIEnv* 和jobject 是必须的,分别表示JNI接口指针和对象本身(类似c++ 的this指针)。

我们不难看出,java的原始数据类型与c类型一一对应,本地方法可以直接访问,java中int对应jint,float对应jfloat等。而非原始类型,则直接对应jobject,比如java中的Object对应jobject,当然为了减少程序出错,引入了jobject的子类,比如jstring,jarray,jintArray等等。

这样以上的头文件也就不难理解了。

下面实现本地方法,注意本地方法实现时需要和头文件一致。

先从input方法说起。

#include <stdio.h>
#include "JNIDemo.h"
JNIEXPORT jstring JNICALL
Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
{
    
}

首先jstring并不是c中常规字符串(char *),而是自己重新封装了下,

因此直接调用printf("%s", prompt)是错误的。必须调用JNI的函数GetStringUTFChars把它转化成c字符串。同样的c字符串(char *)可以调用newStringUTF函数转化成jstring。以下就简单多了。实现如下:

JNIEXPORT jstring JNICALL
Java_Prompt_input(JNIEnv *env, jobject obj, jstring prompt)
{
	char buf[128];
	const char *str = (*env)->GetStringUTFChars(env, prompt, 0);
	printf("%s\n", str);
	(*env)->ReleaseStringUTFChars(env, prompt, str);
	scanf("%s", buf);
	return (*env)->NewStringUTF(env, buf);
}

注意不再使用的jstring,需要手动调用ReleaseXX释放内存。

以下两个方法实现也不难理解,最后的完整版本为:

#include <stdio.h>
#include "JNIDemo.h"
JNIEXPORT jstring JNICALL
Java_JNIDemo_input(JNIEnv *env, jobject obj, jstring prompt)
{
	char buf[128];
	const char *str = (*env)->GetStringUTFChars(env, prompt, 0);
	printf("%s\n", str);
	/* 必须调用释放函数,否则内存泄漏 */
	(*env)->ReleaseStringUTFChars(env, prompt, str);
	scanf("%s", buf);
	return (*env)->NewStringUTF(env, buf);
}
JNIEXPORT jint JNICALL
Java_JNIDemo_sum__II(JNIEnv *env, jobject obj, jint a, jint b)
 {
	  return a + b;
 }
JNIEXPORT jint JNICALL
Java_JNIDemo_sum___3I(JNIEnv *env, jobject obj, jintArray array)
 {
	  int i;
	  int sum = 0;
	  jsize len = (*env)->GetArrayLength(env, array);
	  jint *a = (*env)->GetIntArrayElements(env, array, 0);
	  for (i = 0; i < len; i++)
		  sum += a[i];
	  (*env)->ReleaseIntArrayElements(env, array, a, 0);
	  return sum;
 }

最后编译c文件为动态链接库(windows为dll文件,unix为libXX.so文件),

#!/bin/bash
gcc  -I/usr/lib/jvm/java-7-openjdk-i386/include/ -I/usr/lib/jvm/java-7-openjdk-i386/include/linux/ JNIDemo.c -shared -o libutil.so

然后运行java JNIDemo,输出结果为:Type your name:

Mary
Your name is Mary
3
15

另外注意System.load()必须使用绝对路径,否则会出错,如果你的动态链接库已在环境变量中可以直接调用System.loadLibrary("util");

如果出现java.lang.UnsatisfiedLinkError,则可能路径不对,jvm没有找到对应的动态链接库。

参考:http://journals.ecs.soton.ac.uk/java/tutorial/native1.1/implementing/index.html

转载请注明:http://krystism.is-programmer.com/若有错误,请多多指正,谢谢!
  • 无匹配
  • 无匹配

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter