继续上一篇博文eclipse搭建JNI开发环境,现在我们从代码角度分析,C和Java混合编程时能实现的功能。
使用javah命令,编译生成.h头文件时,每个函数,至少都会有两个参数。JNIEnv 和jclass/jobject。其中,当native方法是静态方法(类方法)时,第二个参数是jclass,当native方法是成员方法时,第二个参数是jobject。其余的参数,会根据你在java文件中声明的方法参数类型,生成具体的签名。jni中类型在jni头文件中定义规则如下:
- typedef union jvalue {
- jboolean z;
- jbyte b;
- jchar c;
- jshort s;
- jint i;
- jlong j;
- jfloat f;
- jdouble d;
- jobject l;
- } 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方法。
1.调用C语言的printf函数,输出固定内容。
public static native void print();
2.转入指定字符串,用printf函数输出。
public static native void print(String str);
3.用C语言实现拼接字符串的功能,并返回给java。
public static native String append(String str);
4.传入字符串,作为Test类构造函数的函数,C语言调用Java类的构造函数,生成jobject,操纵Test类的所有方法和属性。
public native void test(String test);
5.传入Test类的对象,操纵操纵Test类的所有方法和属性。
public native void test(Test test);
6.将传入的字节数组转16进制字符串返回。
public native String toHex(byte[] test);
7.将传入的字符串转成16进制字节数组返回。
public native byte[] toBytes(String test);
完整示例代码如下:
com_flueky_jni_Jni.h
-
- #include <jni.h>
-
-
- #ifndef _Included_com_flueky_jni_Jni
- #define _Included_com_flueky_jni_Jni
- #ifdef __cplusplus
- extern "C" {
- #endif
-
-
-
-
-
- JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_print__
- (JNIEnv *, jclass);
-
-
-
-
-
-
- JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_print__Ljava_lang_String_2
- (JNIEnv *, jclass, jstring);
-
-
-
-
-
-
- JNIEXPORT jstring JNICALL Java_com_flueky_jni_Jni_append
- (JNIEnv *, jclass, jstring);
-
-
-
-
-
-
- JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_test__Ljava_lang_String_2
- (JNIEnv *, jobject, jstring);
-
-
-
-
-
-
- JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_test__Lcom_flueky_jni_Test_2
- (JNIEnv *, jobject, jobject);
-
-
-
-
-
-
- JNIEXPORT jstring JNICALL Java_com_flueky_jni_Jni_toHex
- (JNIEnv *, jobject, jbyteArray);
-
-
-
-
-
-
- JNIEXPORT jbyteArray JNICALL Java_com_flueky_jni_Jni_toBytes
- (JNIEnv *, jobject, jstring);
-
- #ifdef __cplusplus
- }
- #endif
- #endif
main.cpp
-
-
-
-
-
-
-
- #include <stdio.h>
- #include "com_flueky_jni_Jni.h"
- #include <jni.h>
- #include <stdlib.h>
- #include <string.h>
-
-
-
- void operate_test(JNIEnv *env, jobject obj) {
-
- jclass test_cls = env->GetObjectClass(obj);
-
- jmethodID get_mid = env->GetMethodID(test_cls, "getTest",
- "()Ljava/lang/String;");
-
- jstring test = (jstring) env->CallObjectMethod(obj, get_mid);
- jsize len = env->GetStringUTFLength(test);
- const char* str = env->GetStringUTFChars(test, JNI_FALSE);
- char* result = (char*) malloc(sizeof(char) * (len + 1));
- strcpy(result, str);
-
- *(result + len) = 0;
- printf("getTest 输出:%s\n", result);
-
- jmethodID append_mid = env->GetMethodID(test_cls, "append",
- "(Ljava/lang/String;)V");
- env->CallVoidMethod(obj, append_mid, env->NewStringUTF("append test"));
- printf("append: append test\n");
-
- jfieldID test_fid = env->GetFieldID(test_cls, "test", "Ljava/lang/String;");
-
- test = (jstring) env->GetObjectField(obj, test_fid);
- len = env->GetStringUTFLength(test);
- str = env->GetStringUTFChars(test, JNI_FALSE);
- result = (char*) malloc(sizeof(char) * (len + 1));
- strcpy(result, str);
-
- *(result + len) = 0;
- printf("append 结果:%s\n", result);
-
-
- jmethodID print_mid = env->GetStaticMethodID(test_cls, "print",
- "(ICFZLjava/lang/String;)V");
-
- env->CallStaticVoidMethod(test_cls, print_mid, 1, 'c', 1.2f, true, test);
-
-
- env->DeleteLocalRef(obj);
- env->DeleteLocalRef(test);
-
- }
-
- JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_print__(JNIEnv *env,
- jclass cls) {
- printf("小飞哥0217\n");
- }
-
- JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_print__Ljava_lang_String_2(
- JNIEnv *env, jclass cls, jstring jstr) {
-
- jsize len = env->GetStringUTFLength(jstr);
- const char* str = env->GetStringUTFChars(jstr, JNI_FALSE);
- char* result = (char*) malloc(sizeof(char) * (len + 1));
- strcpy(result, str);
-
- *(result + len) = 0;
- printf("本地输出:%s", result);
- }
-
- JNIEXPORT jstring JNICALL Java_com_flueky_jni_Jni_append(JNIEnv *env, jclass,
- jstring jstr) {
-
- jsize len = env->GetStringUTFLength(jstr);
-
- const char* str = env->GetStringUTFChars(jstr, JNI_FALSE);
-
- char* result = (char*) malloc(sizeof(char) * (len + 7 + 1));
-
- strcpy(result, "append ");
- strcpy(result + 7, str);
-
- *(result + 7 + len) = 0;
- return env->NewStringUTF(result);
- }
-
-
-
-
- JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_test__Ljava_lang_String_2(
- JNIEnv *env, jobject obj, jstring jstr) {
-
- jclass test_cls = env->FindClass("com/flueky/jni/Test");
-
- jmethodID init_mid = env->GetMethodID(test_cls, "<init>",
- "(Ljava/lang/String;)V");
-
- jobject test_obj = env->NewObject(test_cls, init_mid, jstr);
-
- operate_test(env, test_obj);
-
- }
- JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_test__Lcom_flueky_jni_Test_2(
- JNIEnv *env, jobject obj, jobject test_obj) {
- operate_test(env, test_obj);
- }
-
- JNIEXPORT jstring JNICALL Java_com_flueky_jni_Jni_toHex(JNIEnv *env,
- jobject jobj, jbyteArray jbytes) {
-
- jsize len = env->GetArrayLength(jbytes);
-
- char *result = (char *) malloc(sizeof(char) * (len * 2 + 1));
-
- jbyte* temp = (jbyte *) malloc(sizeof(jbyte) * len);
-
- env->GetByteArrayRegion(jbytes, 0, len, temp);
-
- for (int i = 0; i < len; i++) {
- *(result + i * 2) = ((*(temp + i) >> 4) & 0xf) + '0';
- *(result + i * 2 + 1) = (*(temp + i) & 0xf) + '0';
- }
-
- free(temp);
- *(result + len * 2) = 0;
-
- jstring str = env->NewStringUTF(result);
- free(result);
- return str;
- }
-
- JNIEXPORT jbyteArray JNICALL Java_com_flueky_jni_Jni_toBytes(JNIEnv *env,
- jobject jobj, jstring jstr) {
-
- jsize len = env->GetStringUTFLength(jstr);
-
- jbyteArray jbytes = env->NewByteArray(len * 2);
-
- const jchar * temp = env->GetStringChars(jstr, JNI_FALSE);
-
- char *result = (char *) malloc(sizeof(char) * (len * 2));
-
- for (int i = 0; i < len; i++) {
- *(result + i * 2) = ((*(temp + i) >> 4) & 0xf) + '0';
- *(result + i * 2 + 1) = (*(temp + i) & 0xf) + '0';
- }
-
- env->SetByteArrayRegion(jbytes, 0, len * 2, (const jbyte *) result);
- free(result);
- return jbytes;
- }
Jni.java
- package com.flueky.jni;
-
- public class Jni {
-
- static {
- System.loadLibrary("JNI_CPP");
- }
-
-
-
-
-
-
-
- public static native void print();
-
-
-
-
-
-
-
-
- public static native void print(String str);
-
-
-
-
-
-
-
-
-
- public static native String append(String str);
-
-
-
-
-
-
-
-
- public native void test(String test);
-
-
-
-
-
-
-
-
- public native void test(Test test);
-
-
-
-
-
-
-
-
-
- public native String toHex(byte[] test);
-
-
-
-
-
-
-
-
-
- public native byte[] toBytes(String test);
-
- }
Test.java
- package com.flueky.jni;
-
- public class Test {
-
- private String test;
-
- public Test(String test) {
- super();
- this.test = test;
- }
-
- public String getTest() {
- return test;
- }
-
- public void append(String str) {
- this.test = test + " " + str;
- }
-
-
-
-
-
-
-
-
- public static void print(int i, char c, float f, boolean z, String test) {
- System.out.println(String.format("Test printf:int = %d,char = %c,float = %.2f,boolean = %s,test = %s", i, c, f,
- z + "", test));
- }
-
- }
main.java
- package com.flueky.jni;
-
- public class Main {
-
- public static void main(String[] args) {
-
- Jni.print();
- Jni.print("csdn 测试");
- System.out.println(Jni.append("flueky"));
-
- Jni jni = new Jni();
-
- jni.test(new Test("小飞哥0217"));
- jni.test("CSCN 测试");
-
- System.out.println(new String(jni.toBytes("ABCDE")));
- System.out.println(jni.toHex("12345".getBytes()));
- }
-
- }
Jni方法说明:
1.获取jclass对象:
a.env->FindClass("com/flueky/jni/Test");注意,这里不是类的签名。
b.env->GetObjectClass(obj);
2.获取方法id:
a.env->GetMethodID(test_cls, "getTest","()Ljava/lang/String;");//获取成员方法id
b.env->GetStaticMethodID(test_cls, "print","(ICFZLjava/lang/String;)V");//获取静态方法id
第一个参数,jclass对象,第二个参数方法名称,第三个参数,方法签名
3.调用方法:
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方法中的参数。
4.获取属性id:
a.env->GetFieldID(test_cls, "test", "Ljava/lang/String;");//获取成员属性id
b.env->GetStaticFieldID(test_cls, "test", "Ljava/lang/String;");//获取静态属性id,程序里没用到。
第一个参数jclass,第二个参数属性名称,第三个参数属性签名
5.获取属性值:
a.env->GetObjectField(obj, test_fid);
第一个参数,jobject,第二个参数,属性id
b.env->GetStaticObjectField(test_cls, test_fid);
第一个参数,jclass,第二个参数,属性id
6.生成jobject对象,通常都是从Java方法中传递过来,还有一种情况是调用java的构造方法来生成jobject对象。
获取构造方法id,env->GetMethodID(test_cls, "<init>","(Ljava/lang/String;)V");
第一个参数jclass,第二个参数构造方法名称(固定<init>),第三个参数构造方法签名(返回类型固定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,其他类型的函数均有返回值。
- typedef jobject jstring;
- typedef jobject jarray;
- typedef jarray jbooleanArray;
- typedef jarray jbyteArray;
- typedef jarray jcharArray;
- typedef jarray jshortArray;
- typedef jarray jintArray;
- typedef jarray jlongArray;
- typedef jarray jfloatArray;
- typedef jarray jdoubleArray;
- typedef jarray jobjectArray;
参照在Jni头文件中的定义,Object类型的函数,返回值是jobject,可以根据实际情况转成以上类型。