java jni 入门6 - 调用Java方法 静态和非静态
参考:《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 ", 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之后即可。
- 上一篇: android NDK JNI so文件的制作和使用
- 下一篇:没有了