一个例子掌握JNI开发

继续上一篇博文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类型类型签名
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

 

/* 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 __cplusplus
extern "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) {
	//根据对象,获取到jclass
	jclass test_cls = env->GetObjectClass(obj);
	//获取成员方法id
	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);
	//获取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 GetStaticFieldID
	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);

	//获取静态方法id
	jmethodID 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>,返回类型void
	jmethodID 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;
	//生成jstring
	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);
	//将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语言实现
	 * 
	 * @date 2016年3月22日 下午4:23:00
	 */
	public static native void print();

	/**
	 * 本地方法,用C语言实现
	 * 
	 * @date 2016年3月22日 下午6:10:43
	 * @param str
	 */
	public static native void print(String str);

	/**
	 * 拼接字符传并返回
	 * 
	 * @date 2016年3月22日 下午6:12:03
	 * @param str
	 * @return
	 */
	public static native String append(String str);

	/**
	 * 测试操作Test类
	 * 
	 * @date 2016年3月22日 下午6:16:06
	 * @param test
	 */
	public native void test(String test);

	/**
	 * 测试操作Test
	 * 
	 * @date 2016年3月22日 下午6:16:59
	 * @param test
	 */
	public native void test(Test test);

	/**
	 * 将test 转16进制
	 * 
	 * @date 2016年3月22日 下午6:25:06
	 * @param test
	 * @return
	 */
	public native String toHex(byte[] test);

	/**
	 * 将test转字节
	 * 
	 * @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;
	}

	/**
	 * 测试调用静态方法,多参数
	 * 
	 * @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();// 小飞哥0217
		Jni.print("csdn 测试");// 本地输出:csdn 测试
		System.out.println(Jni.append("flueky"));// 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,可以根据实际情况转成以上类型。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值