一个掌握JNI开发的例子

继续上一篇博文eclipse搭建JNI开发环境,现在我们从代码角度分析,C和Java混合编程时能实现的功能。

使用javah命令,编译生成.h头文件时,每个函数,至少都会有两个参数。JNIEnv 和jclass/jobject。其中,当native方法是静态方法(类方法)时,第二个参数是jclass,当native方法是成员方法时,第二个参数是jobject。其余的参数,会根据你在java文件中声明的方法参数类型,生成具体的签名。jni中类型在jni头文件中定义规则如下:

[cpp]  view plain  copy
  1. typedef union jvalue {  
  2.     jboolean z;  
  3.     jbyte    b;  
  4.     jchar    c;  
  5.     jshort   s;  
  6.     jint     i;  
  7.     jlong    j;  
  8.     jfloat   f;  
  9.     jdouble  d;  
  10.     jobject  l;  
  11. } jvalue;  
对应签名:
java类型jni类型类型签名
charjcharC
intjintI
longjlongJ
floatjfloatF
doublejdoubleD
booleanjbooleanZ
bytejbyteB
shortjshortS
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

[cpp]  view plain  copy
  1. /* DO NOT EDIT THIS FILE - it is machine generated */  
  2. #include <jni.h>  
  3. /* Header for class com_flueky_jni_Jni */  
  4.   
  5. #ifndef _Included_com_flueky_jni_Jni  
  6. #define _Included_com_flueky_jni_Jni  
  7. #ifdef __cplusplus  
  8. extern "C" {  
  9. #endif  
  10. /* 
  11.  * Class:     com_flueky_jni_Jni 
  12.  * Method:    print 
  13.  * Signature: ()V 
  14.  */  
  15. JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_print__  
  16.   (JNIEnv *, jclass);  
  17.   
  18. /* 
  19.  * Class:     com_flueky_jni_Jni 
  20.  * Method:    print 
  21.  * Signature: (Ljava/lang/String;)V 
  22.  */  
  23. JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_print__Ljava_lang_String_2  
  24.   (JNIEnv *, jclass, jstring);  
  25.   
  26. /* 
  27.  * Class:     com_flueky_jni_Jni 
  28.  * Method:    append 
  29.  * Signature: (Ljava/lang/String;)Ljava/lang/String; 
  30.  */  
  31. JNIEXPORT jstring JNICALL Java_com_flueky_jni_Jni_append  
  32.   (JNIEnv *, jclass, jstring);  
  33.   
  34. /* 
  35.  * Class:     com_flueky_jni_Jni 
  36.  * Method:    test 
  37.  * Signature: (Ljava/lang/String;)V 
  38.  */  
  39. JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_test__Ljava_lang_String_2  
  40.   (JNIEnv *, jobject, jstring);  
  41.   
  42. /* 
  43.  * Class:     com_flueky_jni_Jni 
  44.  * Method:    test 
  45.  * Signature: (Lcom/flueky/jni/Test;)V 
  46.  */  
  47. JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_test__Lcom_flueky_jni_Test_2  
  48.   (JNIEnv *, jobject, jobject);  
  49.   
  50. /* 
  51.  * Class:     com_flueky_jni_Jni 
  52.  * Method:    toHex 
  53.  * Signature: ([B)Ljava/lang/String; 
  54.  */  
  55. JNIEXPORT jstring JNICALL Java_com_flueky_jni_Jni_toHex  
  56.   (JNIEnv *, jobject, jbyteArray);  
  57.   
  58. /* 
  59.  * Class:     com_flueky_jni_Jni 
  60.  * Method:    toBytes 
  61.  * Signature: (Ljava/lang/String;)[B 
  62.  */  
  63. JNIEXPORT jbyteArray JNICALL Java_com_flueky_jni_Jni_toBytes  
  64.   (JNIEnv *, jobject, jstring);  
  65.   
  66. #ifdef __cplusplus  
  67. }  
  68. #endif  
  69. #endif  
main.cpp
[cpp]  view plain  copy
  1. /* 
  2.  * main.cpp 
  3.  * 
  4.  *  Created on: 2016年3月22日 
  5.  *      Author: flueky 
  6.  */  
  7.   
  8. #include <stdio.h>  
  9. #include "com_flueky_jni_Jni.h"  
  10. #include <jni.h>  
  11. #include <stdlib.h>  
  12. #include <string.h>  
  13. /** 
  14.  * 操作test类的对象 
  15.  */  
  16. void operate_test(JNIEnv *env, jobject obj) {  
  17.     //根据对象,获取到jclass  
  18.     jclass test_cls = env->GetObjectClass(obj);  
  19.     //获取成员方法id  
  20.     jmethodID get_mid = env->GetMethodID(test_cls, "getTest",  
  21.             "()Ljava/lang/String;");  
  22.     //回调成员方法  
  23.     jstring test = (jstring) env->CallObjectMethod(obj, get_mid);  
  24.     jsize len = env->GetStringUTFLength(test);  
  25.     const char* str = env->GetStringUTFChars(test, JNI_FALSE);  
  26.     char* result = (char*) malloc(sizeof(char) * (len + 1));  
  27.     strcpy(result, str);  
  28.     //标志结束  
  29.     *(result + len) = 0;  
  30.     printf("getTest 输出:%s\n", result);  
  31.     //获取append方法id,调用append方法  
  32.     jmethodID append_mid = env->GetMethodID(test_cls, "append",  
  33.             "(Ljava/lang/String;)V");  
  34.     env->CallVoidMethod(obj, append_mid, env->NewStringUTF("append test"));  
  35.     printf("append: append test\n");  
  36.     //获取成员变量id,类变量id GetStaticFieldID  
  37.     jfieldID test_fid = env->GetFieldID(test_cls, "test""Ljava/lang/String;");  
  38.     //获取成员变量值  
  39.     test = (jstring) env->GetObjectField(obj, test_fid);  
  40.     len = env->GetStringUTFLength(test);  
  41.     str = env->GetStringUTFChars(test, JNI_FALSE);  
  42.     result = (char*) malloc(sizeof(char) * (len + 1));  
  43.     strcpy(result, str);  
  44.     //标志结束  
  45.     *(result + len) = 0;  
  46.     printf("append 结果:%s\n", result);  
  47.   
  48.     //获取静态方法id  
  49.     jmethodID print_mid = env->GetStaticMethodID(test_cls, "print",  
  50.             "(ICFZLjava/lang/String;)V");  
  51.     //调用静态方法  
  52.     env->CallStaticVoidMethod(test_cls, print_mid, 1, 'c', 1.2f, true, test);  
  53.   
  54.     //删除obj对象  
  55.     env->DeleteLocalRef(obj);  
  56.     env->DeleteLocalRef(test);  
  57.   
  58. }  
  59.   
  60. JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_print__(JNIEnv *env,  
  61.         jclass cls) {  
  62.     printf("小飞哥0217\n");  
  63. }  
  64.   
  65. JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_print__Ljava_lang_String_2(  
  66.         JNIEnv *env, jclass cls, jstring jstr) {  
  67.   
  68.     jsize len = env->GetStringUTFLength(jstr);  
  69.     const char* str = env->GetStringUTFChars(jstr, JNI_FALSE);  
  70.     char* result = (char*) malloc(sizeof(char) * (len + 1));  
  71.     strcpy(result, str);  
  72.     //标志结束  
  73.     *(result + len) = 0;  
  74.     printf("本地输出:%s", result);  
  75. }  
  76.   
  77. JNIEXPORT jstring JNICALL Java_com_flueky_jni_Jni_append(JNIEnv *env, jclass,  
  78.         jstring jstr) {  
  79.     //获取jstring 的长度  
  80.     jsize len = env->GetStringUTFLength(jstr);  
  81.     //jstring 转字符串数组  
  82.     const char* str = env->GetStringUTFChars(jstr, JNI_FALSE);  
  83.     //分配结果字符串空间  
  84.     char* result = (char*) malloc(sizeof(char) * (len + 7 + 1));  
  85.     //字符串函数处理  
  86.     strcpy(result, "append ");  
  87.     strcpy(result + 7, str);  
  88.     //标志结束  
  89.     *(result + 7 + len) = 0;  
  90.     return env->NewStringUTF(result);  
  91. }  
  92.   
  93. /** 
  94.  * 操作test类 
  95.  */  
  96. JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_test__Ljava_lang_String_2(  
  97.         JNIEnv *env, jobject obj, jstring jstr) {  
  98.     //Test类  
  99.     jclass test_cls = env->FindClass("com/flueky/jni/Test");  
  100.     //Test类的构造方法id,构造方法名固定<init>,返回类型void  
  101.     jmethodID init_mid = env->GetMethodID(test_cls, "<init>",  
  102.             "(Ljava/lang/String;)V");  
  103.     //创建Test对象  
  104.     jobject test_obj = env->NewObject(test_cls, init_mid, jstr);  
  105.   
  106.     operate_test(env, test_obj);  
  107.   
  108. }  
  109. JNIEXPORT void JNICALL Java_com_flueky_jni_Jni_test__Lcom_flueky_jni_Test_2(  
  110.         JNIEnv *env, jobject obj, jobject test_obj) {  
  111.     operate_test(env, test_obj);  
  112. }  
  113.   
  114. JNIEXPORT jstring JNICALL Java_com_flueky_jni_Jni_toHex(JNIEnv *env,  
  115.         jobject jobj, jbyteArray jbytes) {  
  116.     //获取字符串长度  
  117.     jsize len = env->GetArrayLength(jbytes);  
  118.     //分配结果的内存  
  119.     char *result = (char *) malloc(sizeof(char) * (len * 2 + 1));  
  120.     //分配缓存的内存  
  121.     jbyte* temp = (jbyte *) malloc(sizeof(jbyte) * len);  
  122.     //从字节数组中取字符  
  123.     env->GetByteArrayRegion(jbytes, 0, len, temp);  
  124.     //转16进制  
  125.     for (int i = 0; i < len; i++) {  
  126.         *(result + i * 2) = ((*(temp + i) >> 4) & 0xf) + '0';  
  127.         *(result + i * 2 + 1) = (*(temp + i) & 0xf) + '0';  
  128.     }  
  129.     //释放缓存的内存  
  130.     free(temp);  
  131.     *(result + len * 2) = 0;  
  132.     //生成jstring  
  133.     jstring str = env->NewStringUTF(result);  
  134.     free(result);  
  135.     return str;  
  136. }  
  137.   
  138. JNIEXPORT jbyteArray JNICALL Java_com_flueky_jni_Jni_toBytes(JNIEnv *env,  
  139.         jobject jobj, jstring jstr) {  
  140.     //获取字符串长度  
  141.     jsize len = env->GetStringUTFLength(jstr);  
  142.     //分配字节数组空间  
  143.     jbyteArray jbytes = env->NewByteArray(len * 2);  
  144.     //将jstring转成字符数组  
  145.     const jchar * temp = env->GetStringChars(jstr, JNI_FALSE);  
  146.     //分配结果的内存  
  147.     char *result = (char *) malloc(sizeof(char) * (len * 2));  
  148.     //转16进制  
  149.     for (int i = 0; i < len; i++) {  
  150.         *(result + i * 2) = ((*(temp + i) >> 4) & 0xf) + '0';  
  151.         *(result + i * 2 + 1) = (*(temp + i) & 0xf) + '0';  
  152.     }  
  153.     //将字符存到字节数组里  
  154.     env->SetByteArrayRegion(jbytes, 0, len * 2, (const jbyte *) result);  
  155.     free(result);  
  156.     return jbytes;  
  157. }  
Jni.java
[java]  view plain  copy
  1. package com.flueky.jni;  
  2.   
  3. public class Jni {  
  4.   
  5.     static {  
  6.         System.loadLibrary("JNI_CPP");  
  7.     }  
  8.   
  9.     /** 
  10.      * 本地方法,用C语言实现 
  11.      *  
  12.      * @author flueky zkf@yitong.com.cn 
  13.      * @date 2016年3月22日 下午4:23:00 
  14.      */  
  15.     public static native void print();  
  16.   
  17.     /** 
  18.      * 本地方法,用C语言实现 
  19.      *  
  20.      * @author flueky zkf@yitong.com.cn 
  21.      * @date 2016年3月22日 下午6:10:43 
  22.      * @param str 
  23.      */  
  24.     public static native void print(String str);  
  25.   
  26.     /** 
  27.      * 拼接字符传并返回 
  28.      *  
  29.      * @author flueky zkf@yitong.com.cn 
  30.      * @date 2016年3月22日 下午6:12:03 
  31.      * @param str 
  32.      * @return 
  33.      */  
  34.     public static native String append(String str);  
  35.   
  36.     /** 
  37.      * 测试操作Test类 
  38.      *  
  39.      * @author flueky zkf@yitong.com.cn 
  40.      * @date 2016年3月22日 下午6:16:06 
  41.      * @param test 
  42.      */  
  43.     public native void test(String test);  
  44.   
  45.     /** 
  46.      * 测试操作Test 
  47.      *  
  48.      * @author flueky zkf@yitong.com.cn 
  49.      * @date 2016年3月22日 下午6:16:59 
  50.      * @param test 
  51.      */  
  52.     public native void test(Test test);  
  53.   
  54.     /** 
  55.      * 将test 转16进制 
  56.      *  
  57.      * @author flueky zkf@yitong.com.cn 
  58.      * @date 2016年3月22日 下午6:25:06 
  59.      * @param test 
  60.      * @return 
  61.      */  
  62.     public native String toHex(byte[] test);  
  63.   
  64.     /** 
  65.      * 将test转字节 
  66.      *  
  67.      * @author flueky zkf@yitong.com.cn 
  68.      * @date 2016年3月22日 下午6:25:17 
  69.      * @param test 
  70.      * @return 
  71.      */  
  72.     public native byte[] toBytes(String test);  
  73.   
  74. }  
Test.java
[java]  view plain  copy
  1. package com.flueky.jni;  
  2.   
  3. public class Test {  
  4.   
  5.     private String test;  
  6.   
  7.     public Test(String test) {  
  8.         super();  
  9.         this.test = test;  
  10.     }  
  11.   
  12.     public String getTest() {  
  13.         return test;  
  14.     }  
  15.   
  16.     public void append(String str) {  
  17.         this.test = test + " " + str;  
  18.     }  
  19.   
  20.     /** 
  21.      * 测试调用静态方法,多参数 
  22.      *  
  23.      * @author flueky zkf@yitong.com.cn 
  24.      * @date 2016年3月22日 下午6:19:13 
  25.      * @param str 
  26.      */  
  27.     public static void print(int i, char c, float f, boolean z, String test) {  
  28.         System.out.println(String.format("Test printf:int = %d,char = %c,float = %.2f,boolean = %s,test = %s", i, c, f,  
  29.                 z + "", test));  
  30.     }  
  31.   
  32. }  

main.java

[java]  view plain  copy
  1. package com.flueky.jni;  
  2.   
  3. public class Main {  
  4.   
  5.     public static void main(String[] args) {  
  6.           
  7.         Jni.print();// 小飞哥0217  
  8.         Jni.print("csdn 测试");// 本地输出:csdn 测试  
  9.         System.out.println(Jni.append("flueky"));// append flueky  
  10.   
  11.         Jni jni = new Jni();  
  12.   
  13.         jni.test(new Test("小飞哥0217"));  
  14.         jni.test("CSCN 测试");  
  15.   
  16.         System.out.println(new String(jni.toBytes("ABCDE")));  
  17.         System.out.println(jni.toHex("12345".getBytes()));  
  18.     }  
  19.   
  20. }  


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,其他类型的函数均有返回值。

[cpp]  view plain  copy
  1. typedef jobject jstring;  
  2. typedef jobject jarray;  
  3. typedef jarray jbooleanArray;  
  4. typedef jarray jbyteArray;  
  5. typedef jarray jcharArray;  
  6. typedef jarray jshortArray;  
  7. typedef jarray jintArray;  
  8. typedef jarray jlongArray;  
  9. typedef jarray jfloatArray;  
  10. typedef jarray jdoubleArray;  
  11. typedef jarray jobjectArray;  
参照在Jni头文件中的定义,Object类型的函数,返回值是jobject,可以根据实际情况转成以上类型。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值