继续上一篇博文[eclipse搭建JNI开发环境](/blog/2016-03-21/),现在我们从代码角度分析,C和Java混合编程时能实现的功能。
jni头文件
使用javah
命令,编译生成.h头文件时,每个函数,至少都会有两个参数。JNIEnv
和jclass/jobject
。其中,当native方法是静态方法(类方法)时,第二个参数是jclass
,当native方法是成员方法时,第二个参数是jobject
。其余的参数,会根据你在java文件中声明的方法参数类型,生成具体的签名。jni中类型在jni头文件中定义规则如下:
1 | typedef union jvalue { |
对应签名:
java类型 | jni类型 | 类型签名 |
---|---|---|
char | jchar | C |
int | jint | I |
long | jlong | J |
float | jfloat | F |
double | jdouble | D |
boolean | jboolean | Z |
byte | jbyte | B |
short | jshort | S |
void | V | |
类 | L全限定名;,比如String, 其签名为Ljava/lang/util/String; | |
数组 | [类型签名, 比如 [B |
Jni.java 文件中,对应7个native方法。
调用C语言的printf函数,输出固定内容。
1
public static native void print();
转入指定字符串,用printf函数输出。
1
public static native void print(String str);
用C语言实现拼接字符串的功能,并返回给java。
1
public static native String append(String str);
传入字符串,作为Test类构造函数的函数,C语言调用Java类的构造函数,生成jobject,操纵Test类的所有方法和属性。
1
public native void test(String test);
传入Test类的对象,操纵操纵Test类的所有方法和属性。
1
public native void test(Test test);
将传入的字节数组转16进制字符串返回。
1
public native String toHex(byte[] test);
将传入的字符串转成16进制字节数组返回。
1
public native byte[] toBytes(String test);
完整示例代码
com_flueky_jni_Jni.h
1 | /* DO NOT EDIT THIS FILE - it is machine generated */ |
main.cpp
1 | /* |
Jni.java
1 | package com.flueky.jni; |
Test.java
1 | package com.flueky.jni; |
main.java
1 | package com.flueky.jni; |
Jni方法说明
获取jclass对象:
a.env->FindClass(“com/flueky/jni/Test”);注意,这里不是类的签名。
b.env->GetObjectClass(obj);
获取方法id:
a.env->GetMethodID(test_cls, “getTest”,”()Ljava/lang/String;”);//获取成员方法id
b.env->GetStaticMethodID(test_cls, “print”,”(ICFZLjava/lang/String;)V”);//获取静态方法id
第一个参数,jclass对象,第二个参数方法名称,第三个参数,方法签名
调用方法:
a.env->CallVoidMethod(obj, append_mid, env->NewStringUTF(“append test”));//调用成员方法
第一个参数jobject,第二个参数方法id,后面参数,依次是Java方法中的参数。
b.env->CallStaticVoidMethod(test_cls, print_mid, 1, ‘c’, 1.2f, true, test);//调用静态方法
第一个参数jclass,第二个参数方法id,后面参数,依次是Java方法中的参数。
获取属性id:
a.env->GetFieldID(test_cls, “test”, “Ljava/lang/String;”);//获取成员属性id
b.env->GetStaticFieldID(test_cls, “test”, “Ljava/lang/String;”);//获取静态属性id,程序里没用到。
第一个参数jclass,第二个参数属性名称,第三个参数属性签名
获取属性值:
a.env->GetObjectField(obj, test_fid);
第一个参数,jobject,第二个参数,属性id
b.env->GetStaticObjectField(test_cls, test_fid);
第一个参数,jclass,第二个参数,属性id
生成jobject对象,通常都是从Java方法中传递过来,还有一种情况是调用java的构造方法来生成jobject对象。
获取构造方法id,env->GetMethodID(test_cls, “
“,”(Ljava/lang/String;)V”); 第一个参数jclass,第二个参数构造方法名称(固定
),第三个参数构造方法签名(返回类型固定void签名V) 生成jobject,env->NewObject(test_cls, init_mid, jstr);
第一个参数jclass,第二个参数构造方法id,后面的参数依次是Java中构造函数的参数。
上述3
和5
调用的jni函数名称中,Char
、Boolean
、Byte
、Int
、Long
、Short
、Float
、Double
、Object
、Void
,可以相互替换,除了Void
,其他类型的函数均有返回值。
1 | typedef jobject jstring; |
参照在Jni头文件中的定义,Object类型的函数,返回值是jobject,可以根据实际情况转成以上类型。