java jni 入门6 - 调用Java方法 静态和非静态

16 篇文章 1 订阅

参考:《Java核心技术 卷II:高级特性》第12章 本地方法


########################################################################


本地方法常常需要从传递给它的对象那里得到某种服务。本文首先使用非静态方法进行操作,然后介绍静态方法


Printf3Test.java

/**
 * @time 15-11-12
 * @author zj
*/
import java.io.*;

class Printf3Test {
	public static void main(String[] args) {
		double price = 44.95;
		double tax = 7.75;
		double amountDue = price * (1 + tax / 100);
		PrintWriter out = new PrintWriter(System.out);
		Printf3.fprint(out, "Amount due = %8.2f\n", amountDue);
		out.flush();
	}
}


Printf3.java

/**
 * @time 15-11-12
 * @author zj
*/
import java.io.*;

class Printf3 {
	public static native void fprint(PrintWriter out, String format, double x);

	static {
		System.loadLibrary("Printf3");
	}
}

Printf3.c

/**
 * @time 15-11-12
 * @author zj
*/

#include "Printf3.h"
#include <string.h>
#include <stdlib.h>
#include <float.h>

/**
 @param format a string containing a printf specifier
	(such as "%8.2f"). Substrings "%%" are skipped.
 @return a pointer to the format specifier (skipping the '%')
	or NULL if there wasn't a unique format specifier	
*/
char* find_format(const char format[]) {
	char *p;
	char *q;

	p=strchr(format, '%');
	while (p != NULL && *(p+1) == '%') /* skip %% */
		p = strchr(p+2, '%');
	if (p == NULL) 
		return NULL;		
	/* now check that % is unique */
	p++;
	q = strchr(p, '%');
	while (q != NULL && *(q+1) == '%') /* skip %% */
		q = strchr(q+2, '%');
	if (q != NULL)
		return NULL; /* not unique */
	q = p + strspn(p, "-0+#"); /* skip past flags */
	q += strspn(q, "0123456789"); /* skip past field width */
	if (*q == '.') {
		q++;
		q += strspn(q, "0123456789");
	}
	/* skip past precision */
	if (strchr("eEfFgG", *q) == NULL)
		return NULL;
	/* not a floating-point format */
	return p;
}

JNIEXPORT void JNICALL Java_Printf3_fprint
  (JNIEnv *env, jclass cl, jobject out, jstring format, jdouble x) {
	const char *cformat;
	char *fmt;
	jstring str;
	jclass class_PrintWriter;
	jmethodID id_print;

	cformat = (*env)->GetStringUTFChars(env, format, NULL);
	fmt = find_format(cformat);
	if (fmt == NULL) 
		str = format;
	else {
		char *cstr;
		int width = atoi(fmt);
		if (width == 0)
			width = DBL_DIG + 10;
		cstr = (char*)malloc(strlen(cformat)+width);
		sprintf(cstr, cformat, x);
		str = (*env)->NewStringUTF(env, cstr);
		free(cstr);
	}
	(*env)->ReleaseStringUTFChars(env, format, cformat);

	/* now call ps.print(str) */

	/* get the class */
	class_PrintWriter = (*env)->GetObjectClass(env, out);
		
	/* get the method ID */
	id_print = (*env)->GetMethodID(env, class_PrintWriter, "print", "(Ljava/lang/String;)V");

	/* call the method */
	(*env)->CallVoidMethod(env, out, id_print, str);
}



详细讲解:


先增强Printf类,给它增加一个与C函数fprintf类似的方法。也就是说,它能够在任意PrintWriter对象上打印一个字符串。

下面是用Java编写的该方法的定义:

class Printf3 {
     .......
     public native static void fprint(PrintWriter out, String s, double x); 
     .......
}

首先把要打印的字符串编成一个String对象str,就像我们在sprint方法中已经实现的那样。然后,从实现本地方法的C函数调用PrintWriter类的print方法。

使用如下函数调用,就可以从C调用任何Java方法:

(*env)->CallXxxMethod(env, implicit parameter, methodID, explicit parameters)
根据方法的返回类型,用Void , Int , Object等来替换Xxx。就像你需要一个fieldID来访问某个对象的一个域,需要一个方法的ID来调用方法。你可以通过调用JNI函数GetMethodID,并且提供该类 , 方法的名字和方法签名来获得方法ID。


PrintWriter类有几个名为print的重载方法。基于这个原因,必须提供一个字符串,描述你想要用的特定函数的参数和返回值。例如,如果想要用

void print(java.lang.String)
必须把签名“编”为字符串"(Ljava/lang/String;)V"


下面是进行方法调用的完整步骤

1.获取隐式参数的类。

2.获取方法ID。

3.进行调用。

/* get the class */
class_PrintWriter = (*env)->GetObjectClass(env, out);

/* get the method ID */
id_print = (*env)->GetMethodID(env, class_PrintWriter, "print", "(Ljava/lang/String;)V");

/* call the method */
(*env)->CallVoidMethod(env, out, id_print, str);

note:数值型的方法ID和域ID在概念上和反射API中的Method和Field对象相似。可以使用以下函数在两者间进行转换:

jobject ToReflectedMethod(JNIEnv *env, jclass class, jmethodID methodID);
//returns Method object
methodID FromReflectedMethodID(JNIEnv *env, jobject method);
jobject ToReflectedField(JNIEnv *env, jclass class, jfieldID fieldID);
//returns Field object
fieldID FromReflectedField(JNIEnv *env, jobject field)

#############################################################################


静态方法


从本地方法调用静态方法与调用非静态方法类似。两者的差别是:

1.要用GetStaticMethodID和CallStaticXxxMethod函数。

2.当调用方法时,要提供类对象,而不是隐含的参数对象。

例如,从本地方法调用以下静态方法:

System.getProperty("java.class.path")
这个调用的返回值是给出了当前类路径的字符串。

首先,必须找到要用的类。因为没有System类的对象可供使用,故使用FindClass而非GetObjectClass:

jclass class_System = (*env)->FindClass(env, "java/lang/System");
接着,需要静态getProperty方法的ID。该方法的编码签名是:

"(Ljava/lang/String;)Ljava/lang/String;"
既然参数和返回值都是字符串。因此,获取方法ID为:

jmethodID id_getProperty = (*env)->GetStaticMethodID(env, class_System, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
最后,进行调用。注意,类对象被传递给了CallStaticObjectMethod函数:

jobject obj_ret = (*env)->CallStaticObjectMethod(env, class_System, id_getProperty, (*env)->NewStringUTF(env, "java.class.path"));
该方法的返回值是jobject类型的。如果想要把它当作字符串操作,必须把它转型为jstring。

jstring str_ret = (jstring)obj_ret;
注意:在C中,jstring和jclass类型同后面将要介绍的数组类型一样,都是与jobject等价的类型。因此,在C语言中,前面例子中的转型并不是严格必需的。当时在C++中,这些类型被定义为对拥有正确继承层次关系的"哑类"的指针。例如,将一个jstring不经过转型便赋给jobject在C++中是合法的,当时将jobject赋给jstring必须先转型。


#################################################################


构造器


本地方法可以通过调用构造器来创建新的Java对象。可以调用NewObject函数来调用构造器。

jobject obj_new = (*env)->NewObject(env, methodID, construction parameters);
可以通过指定方法名为"<init>",并指定构造器(返回值为void)的编码签名,从GetMethodID函数中获取该调用必须的方法ID。例如,使用本地方法创建FileOutputStream对象:

const char[] fileName = "...";
jstring str_fileName = (*env)->NewStringUTF(env, fileName);
jclass class_FileOuputStream = (*env)->FindClass(env, "java/io/FileOutputStream");
jmethodID id_FieldOutputStream = (*env)->GetMethodID(env, class_FileOutputStream, "<init>", "(Ljava/lang/String;)V");
jobject obj_stream = (*env)->NewObject(env, class_FileOutputStream, id_FileOutputStream, str_fileName);
注意,构造器的签名接受一个java.lang.String类型的参数,返回类型为void。


###################################################################3


执行Java方法详解:


jmethodID GetMethodID(JNIEnv *env, jclass cl, const char name[], const char methodSignature[])
返回类中某个方法的标识符


Xxx CallXxxMethod(JNIEnv *env, jobject obj, jmethodID id, args)
调用一个方法。返回类型Xxx是Object , Boolean , Byte , Char , Short , Int , Long , Float 或 Double之一。函数有一个可变的参数个数,只要把方法参数附加到方法ID之后即可。

jmethodID GetStaticMethodID(JNIEnv *env, jclass cl, const char name[], const char methodsignature[])
返回类的某个静态方法的标识符


Xxx CallStaticXxxMethod(JNIEnv *env, jclass cl, jmethodID id, args)
调用一个静态方法。返回类型Xxx是Object , Boolean , Byte , Char , Short , Int , Long , Float 或 Double之一。函数有一个可变的参数个数,只要吧方法参数附加到方法ID之后即可。


jobject NewObject(JNIEnv *env, jclass cl, jmethodID id, args)
调用构造器。函数ID从带有函数名为"<init>"和返回类型为void的GetMethodID获取。函数有一个可变的参数个数,只要吧方法参数附加到方法ID之后即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值