一个例子掌握JNI开发

来源:互联网 发布:什么决定命运知乎 编辑:程序博客网 时间:2024/06/14 11:31

继续上一篇博文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类型类型签名charjcharCintjintIlongjlongJfloatjfloatFdoublejdoubleDbooleanjbooleanZbytejbyteBshortjshortSvoid
V
L全限定名;,比如String, 其签名为Ljava/lang/util/String;数组
[类型签名, 比如 [B

Jni.java 文件中,对应7个native方法。

1.调用C语言的printf函数,输出固定内容。

public staticnative void print();

2.转入指定字符串,用printf函数输出。

public staticnative void print(String str);

3.用C语言实现拼接字符串的功能,并返回给java。

public staticnative String append(String str);

4.传入字符串,作为Test类构造函数的函数,C语言调用Java类的构造函数,生成jobject,操纵Test类的所有方法和属性。

public nativevoid test(String test);

5.传入Test类的对象,操纵操纵Test类的所有方法和属性。

public nativevoid test(Test test);

6.将传入的字节数组转16进制字符串返回。

public native String toHex(byte[]test);

7.将传入的字符串转成16进制字节数组返回。

public nativebyte[] toBytes(String test);


完整示例代码如下:

com_flueky_jni_Jni.h

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_flueky_jni_Jni */#ifndef _Included_com_flueky_jni_Jni#define _Included_com_flueky_jni_Jni#ifdef __cplusplusextern "C" {#endif/* * Class:     com_flueky_jni_Jni * Method:    print * Signature: ()V */JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_print__  (JNIEnv *, jclass);/* * Class:     com_flueky_jni_Jni * Method:    print * Signature: (Ljava/lang/String;)V */JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_print__Ljava_lang_String_2  (JNIEnv *, jclass, jstring);/* * Class:     com_flueky_jni_Jni * Method:    append * Signature: (Ljava/lang/String;)Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_com_flueky_jni_Jni_append  (JNIEnv *, jclass, jstring);/* * Class:     com_flueky_jni_Jni * Method:    test * Signature: (Ljava/lang/String;)V */JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_test__Ljava_lang_String_2  (JNIEnv *, jobject, jstring);/* * Class:     com_flueky_jni_Jni * Method:    test * Signature: (Lcom/flueky/jni/Test;)V */JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_test__Lcom_flueky_jni_Test_2  (JNIEnv *, jobject, jobject);/* * Class:     com_flueky_jni_Jni * Method:    toHex * Signature: ([B)Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_com_flueky_jni_Jni_toHex  (JNIEnv *, jobject, jbyteArray);/* * Class:     com_flueky_jni_Jni * Method:    toBytes * Signature: (Ljava/lang/String;)[B */JNIEXPORT jbyteArray JNICALL Java_com_flueky_jni_Jni_toBytes  (JNIEnv *, jobject, jstring);#ifdef __cplusplus}#endif#endif
main.cpp
/* * main.cpp * *  Created on: 2016年3月22日 *      Author: flueky */#include <stdio.h>#include "com_flueky_jni_Jni.h"#include <jni.h>#include <stdlib.h>#include <string.h>/** * 操作test类的对象 */void operate_test(JNIEnv *env, jobject obj) {//根据对象,获取到jclassjclass test_cls = env->GetObjectClass(obj);//获取成员方法idjmethodID 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);//获取append方法id,调用append方法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");//获取成员变量id,类变量id GetStaticFieldIDjfieldID 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);//获取静态方法idjmethodID print_mid = env->GetStaticMethodID(test_cls, "print","(ICFZLjava/lang/String;)V");//调用静态方法env->CallStaticVoidMethod(test_cls, print_mid, 1, 'c', 1.2f, true, test);//删除obj对象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) {//获取jstring 的长度jsize len = env->GetStringUTFLength(jstr);//jstring 转字符串数组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);}/** * 操作test类 */JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_test__Ljava_lang_String_2(JNIEnv *env, jobject obj, jstring jstr) {//Test类jclass test_cls = env->FindClass("com/flueky/jni/Test");//Test类的构造方法id,构造方法名固定<init>,返回类型voidjmethodID init_mid = env->GetMethodID(test_cls, "<init>","(Ljava/lang/String;)V");//创建Test对象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);//转16进制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;//生成jstringjstring 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);//将jstring转成字符数组const jchar * temp = env->GetStringChars(jstr, JNI_FALSE);//分配结果的内存char *result = (char *) malloc(sizeof(char) * (len * 2));//转16进制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");}/** * 本地方法,用C语言实现 *  * @author flueky zkf@yitong.com.cn * @date 2016年3月22日 下午4:23:00 */public static native void print();/** * 本地方法,用C语言实现 *  * @author flueky zkf@yitong.com.cn * @date 2016年3月22日 下午6:10:43 * @param str */public static native void print(String str);/** * 拼接字符传并返回 *  * @author flueky zkf@yitong.com.cn * @date 2016年3月22日 下午6:12:03 * @param str * @return */public static native String append(String str);/** * 测试操作Test类 *  * @author flueky zkf@yitong.com.cn * @date 2016年3月22日 下午6:16:06 * @param test */public native void test(String test);/** * 测试操作Test *  * @author flueky zkf@yitong.com.cn * @date 2016年3月22日 下午6:16:59 * @param test */public native void test(Test test);/** * 将test 转16进制 *  * @author flueky zkf@yitong.com.cn * @date 2016年3月22日 下午6:25:06 * @param test * @return */public native String toHex(byte[] test);/** * 将test转字节 *  * @author flueky zkf@yitong.com.cn * @date 2016年3月22日 下午6:25:17 * @param test * @return */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;}/** * 测试调用静态方法,多参数 *  * @author flueky zkf@yitong.com.cn * @date 2016年3月22日 下午6:19:13 * @param 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();// 小飞哥0217Jni.print("csdn 测试");// 本地输出:csdn 测试System.out.println(Jni.append("flueky"));// append fluekyJni 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,可以根据实际情况转成以上类型。

1 0
原创粉丝点击