1,在jni C/C++中调用java代码的流程:
1)获取类
2)调用静态方法时,先获取methodID(使用GetStaticMethodID),然后使用类似java invoke的形式将类和methodID作为参数调用CallStaticMethod方法,即实现对java方法的调用。
3)调用非静态方法时,还需要类的实例object,相关的方法有GetMethodID、Call***Method
2,JNIEnv类中有如下几个简单的函数可以取得类(jclass)
1)jclass FindClass(const char* clsName) 根据类名来查找一个类,完整类名。
2)jclass GetObjectClass(jobject obj) 根据一个对象,获取该对象的类
3)jclass GetSuperClass(jclass obj) 获取一个类的父类
FindClass 会在classpath系统环境变量下寻找类,需要传入完整的类名,并注意包与包之间是用"/"而不是"."来分割
如:jclass cls_string= env->FindClass("java/lang/String");
以上相关函数都在jni.h定义。
3,使用GetMethodID获取methodID前需要知道什么?
1)方法签名
使用javap命令来产生签名:
javap -s -p [full class Name]
-s 表示输出签名信息
-p 同-private,输出包括private访问权限的成员信息
比如类TestNative的源码:
package video1;
import java.util.Date;
public class TestNative {
public String name="Test";
public int number =100;
public int signTest(int i,Date date,int[] arr){
System.out.println("Sign Test");
return 0;
}
//native关键字修饰的方法,其内容是C/C++编写的,java中不必为它编写具体的实现
public native void sayHello();
public static void main(String[] args) {
System.loadLibrary("NativeCode");
TestNative tn = new TestNative();
tn.sayHello();
}
}
使用javap命令获取编辑后类的方法签名
javap -s -private video1.TestNative
输出:
Compiled from "TestNative.java"
public class video1.TestNative extends java.lang.Object{
public java.lang.String name;
Signature: Ljava/lang/String;
public video1.TestNative();
Signature: ()V
public int signTest(int, java.util.Date, int[]);
Signature: (ILjava/util/Date;[I)I
public native void sayHello();
Signature: ()V
public static void main(java.lang.String[]);
Signature: ([Ljava/lang/String;)V
}
2)java数据类型对应的签名定义
类型 相应的签名
boolean Z
byte B
char C
short S
int I
long J
float F
double D
void V
object L用/分隔包的完整类名: Ljava/lang/String;
Array [签名 [I [Ljava/lang/Object;
Method (参数1类型签名 参数2类型签名···)返回值类型签名
3)方法签名举例:
void f1() ()V
int f2(int, long) (IJ)I
boolean f3(int[]) ([I)Z
double f4(String, int) (Ljava/lang/String;I)D
void f5(int, String [], char) (I[Ljava/lang/String;C)V
特别注意:Object后面一定有分号(;)结束的,多个对象参数中间也用分号(;)来分隔
4)对于jmethodID GetMethodID(jclass clazz, const char *name, const char *sign)
clazz代表该属性所在的类,name表示方法名称,sign是签名
(什么是签名,签名是对函数参数和返回值的描述。对同一个函数,在java中允许重载,这个时候就需要这个sign来进行区分了)
举例:
char *getAlgorithm(JNIEnv *env, jbyteArray jbyteArrays) {
//获取类java.security.MessageDigest
jclass digest_clazz = (env)->FindClass("java/security/MessageDigest");
if (digest_clazz == NULL) {
return NULL;
}
//获取方法 MessageDigest.getInstance(String algorithm)的ID,这个方法的返回值类型是MessageDigest
//如果类中没有此方法,会返回NULL。但不报异常
jmethodID methodID_getInstance = (env)->GetStaticMethodID(digest_clazz, "getInstance", ""
"(Ljava/lang/String;)Ljava/security/MessageDigest;");
//调用方法 MessageDigest.getInstance(String algorithm),将字符串 "MD5" 作为参数
jobject digest_object = (env)->CallStaticObjectMethod(digest_clazz, methodID_getInstance,
env->NewStringUTF("MD5"));
//获取方法 void update(byte[] bytes)的ID
jmethodID methodID_update = (env)->GetMethodID(digest_clazz, "update", "([B)V");
//调用上述方法,jbyteArrays作为字节数组类型的参数
(env)->CallVoidMethod(digest_object, methodID_update, jbyteArrays);
//获取方法 byte[] digest()的ID
jmethodID methodID_digest = (env)->GetMethodID(digest_clazz, "digest", "()[B");
//调用上述方法
jbyteArray digestArray = (jbyteArray) (env)->CallObjectMethod(digest_object, methodID_digest);
jbyte *array = (env)->GetByteArrayElements(digestArray, NULL);
jsize len = (env)->GetArrayLength(digestArray);
unsigned char *encrypt = (unsigned char *) calloc((size_t) len + 1, sizeof(unsigned char));
for (int j = 0; j < len; ++j) {
encrypt[j] = (unsigned char) array[j];
}
encrypt[len] = '\0';
(env)->ReleaseByteArrayElements(digestArray, array, 0);
return getHexChar(encrypt, len);
}
参考:
http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/jniTOC.html
https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html
https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/jniTOC.html