编撰android程序调用jni本地方法的实现(详细例子)

编写android程序调用jni本地方法的实现(详细例子)

在写android程序的时候会用到jni,接下来的代码讲诉C实现,环境配置请看我其他的博客,不多说,直接上代码,代码上几乎每一句都会解释,绝对易懂

#include "com_ndk_test_JniClient.h"
#include <stdlib.h>
//#include<jni.h>
#include <stdio.h>
#define ARRAY_LENGTH 5//宏定义
#ifdef __cplusplus
extern "C"
{
#endif
/*
 * Class:     com_ndk_test_JniClient
 * Method:    AddStr
 * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_ndk_test_JniClient_AddStr(JNIEnv *env,
		jclass arg, jstring a, jstring b) {
	//JNIEnv也是在jni.h中定义的,代表JNI调用的上下文,
	//GetStringUTFChars()、ReleaseStringUTFChars()和NewStringUTF()均是JNIEnv的函数。

	//jstring str = (*env)->NewStringUTF(env, "HelloWorld from JNI !");
	//从JNI调用上下文中获取UTF编码的输入字符,将其放在指针str所指向 的一段内存中
	const char* str = (*env)->GetStringUTFChars(env, a, 0); //这是c的写法,接下来没有注释的都是有c编写
	//const char* s1 = env->GetStringUTFChars(a, 0);//这是c++的写法,以下的方法都是这种形式
	char cap[128]; //定义char数组
	strcpy(cap, str); //复制字符串到cap
	(*env)->ReleaseStringUTFChars(env, a, str); //释放这段内存,个人认为这个像c++虚函数
	int i = 0;
	for (i = 0; i < strlen(cap); i++)
		*(cap + i) = (char) toupper(*(cap + i)); //将经过大写转换的字符串予以返回
	return (*env)->NewStringUTF(env, cap); //C语言的字符串指针 转换为JNI的jstring类型
//	return (*env)->NewStringUTF(env, strupr(cap));
//	return str;
}
/*
 * Class:     com_ndk_test_JniClient
 * Method:    AddInt
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_ndk_test_JniClient_AddInt(JNIEnv *env,
		jclass arg, jint a, jint b) {
	return a + b;
}
;
//TODO
JNIEXPORT jint JNICALL Java_com_ndk_test_JniClient_intArrayMethord(JNIEnv * env,
		jclass clazz, jintArray array) {
//	return 200;
	int i, sum = 0;
	jsize len = (*env)->GetArrayLength(env, array);
	jint *body = (*env)->GetIntArrayElements(env, array, 0);
	for (i = 0; i < len; i++) {
		sum += body[i];
	}
	(*env)->ReleaseIntArrayElements(env, array, body, 0);
	return sum;

}
JNIEXPORT jboolean JNICALL Java_com_ndk_test_JniClient_booleanMethord(
		JNIEnv *env, jclass clazz, jboolean b) {
	return b;
}

/**
 *
 * 以下代码需要在java程序中声明,头文件中也没有改写
 * */
JNIEXPORT jintArray JNICALL Java_com_ndk_test_JniClient_intMethord(JNIEnv *env,
		jclass clazz) {
	int i = 1; //jint是在JNI框 架内特有的整数类型
	jintArray array; //定义数组对象
	array = (*env)->NewIntArray(env, 10); //开辟出一个长度为10 的jint数组
	for (; i <= 10; i++) //依次向该数组中放入元素1-10
		(*env)->SetIntArrayRegion(env, array, i - 1, 1, &i);
	//演示GetArrayLength() 和GetIntArrayElements()这两个函数的使用方法
	/* 获取数组对象的元素个数 */
	int len = (*env)->GetArrayLength(env, array);
	/* 获取数组中的所有元素 */
	jint* elems = (*env)->GetIntArrayElements(env, array, 0);
	for (i = 0; i < len; i++)
		printf("ELEMENT %d IS %d/n", i, elems[i]);
	return array;
}
/**
 * 所有的本地方法的第一个参数都是指向JNIEnv结构的。
 * 这个结构是用来调用JNI函数的。第二个参数jclass的意义,
 * 要看方法是不是静态的(static)或者实例(Instance)的。
 * 前者,jclass代表一个类对象的引用,而后者是被调用的方法所属对象的引用。
 * 深化:虽然仍然是传递数组,但是数组的基类换成了字符串这样一种对象数据类型。
 * Java程序将向C程序传入一个包含中文字符的字符串,
 * C程序并没有处理这个字符串,而是开辟出一个新的字符串数组返回给Java程序,其中还包含两个汉字字符串。
 * java写法----->: public native String[] stringMethod(String text);
 public static void main(String[] args)
 throws java.io.UnsupportedEncodingException
 {
 System.loadLibrary("Sample3");
 Sample3 sample = new Sample3();
 String[] texts = sample.stringMethod("java编程思想");
 for(int i=0;i<texts.length;i++)
 {
 texts[i]=new String(texts[i].getBytes("ISO8859-1"),"GBK");
 System.out.print( texts[i] );
 }
 System.out.println();
 }
 * */

JNIEXPORT jobjectArray JNICALL Java_com_ndk_test_JniClient_stringMethord(
		JNIEnv *env, jclass obj, jstring string) {
	//通过FindClass()函数在JNI上下文中获取到java.lang.String的类型 (Class),并将其赋予jclass变量
	jclass objClass = (*env)->FindClass(env, "java/lang/String");
	//JNI框架并 没有定义专门的字符串数组,
	//而是使用jobjectArray——对象数组,
	//对象数组的基类是jclass,jclass是JNI框架内特有的类型,
	//相当 于Java语言中的Class类型
	//定义了一个长度为5的对象数组texts
	jobjectArray texts = (*env)->NewObjectArray(env, (jsize) ARRAY_LENGTH,
			objClass, 0);
	jstring jstr;
	char* sa[] = { "Hello,", "world!", "JNI", "很", "好玩" };
	int i = 0;
	//循环放入预先定义好的sa数组中的字符串,
	//当然前置条件是使用NewStringUTF()函数将C语言的字符串转换为jstring类型
	for (; i < ARRAY_LENGTH; i++) {
		jstr = (*env)->NewStringUTF(env, sa[i]);
		(*env)->SetObjectArrayElement(env, texts, i, jstr); //必须放入jstring
	}
	//中文字符则是用支持GBK的输入法输入的,而Java程序采用 ISO8859_1字符集存放JNI调用的返回字符
	return texts;
}
/**
 * 演示的是C程序向Java程序传递对象数组,
 * 而且对象数组中存放的不再是字符串,
 * 而是一个在Java中自定义的、含有一个topic属性的MailInfo对象类型
 *MailInfo对象定义如下:->
 public class MailInfo {
 public String topic;
 public String getTopic()
 {
 return this.topic;
 }
 public void setTopic(String topic)
 {
 this.topic=topic;
 }
 }
 Java程序的源代码如下:->
 public class Sample4
 {
 public native MailInfo[] objectMethod(String text);
 public static void main(String[] args)
 {
 System.loadLibrary("Sample4");
 Sample4 sample = new Sample4();
 MailInfo[] mails = sample.objectMethod("Thinking In Java");
 for(int i=0;i<mails.length;i++)
 System.out.println(mails[i].topic);
 }
 }
 * */

JNIEXPORT jboolean JNICALL Java_com_ndk_test_JniClient_objectMethod(JNIEnv *env,
		jobject obj, jstring string) {
	//这次通过FindClass()函数在JNI上下文中获取的是java.lang.Object的类型(Class),
	//并将 其作为基类开辟出一个长度为5的对象数组( #define ARRAY_LENGTH 5),准备用来存放MailInfo对象。
	class objClass = (*env)->FindClass(env, "java/lang/Object");
	jobjectArray mails = (*env)->NewObjectArray(env, (jsize) ARRAY_LENGTH,
			objClass, 0);
	jclass objectClass = (*env)->FindClass(env, "MailInfo");
	//创建一个jfieldID类型的变量,在JNI中,操作对 象属性都是通过jfieldID进行的.
	//首先查找得到MailInfo的类型(Class),
	//然后基于这个jclass进一步获取其名为 topic的属性,并将其赋予jfieldID变量。
	jfieldID topicFieldId = (*env)->GetFieldID(env, objectClass, "topic",
			"Ljava/lang/String;");
	int i = 0;
	//循环向对象数组中放入jobject对象。 SetObjectField()函数属于首次使用,
	//该函数的作用是向jobject的属性赋值,而值的内容正是Java程序传入的jstring变量 值。
	//请注意在向对象属性赋值和向对象数组中放入对象的过程中,
	//我们使用了在函数头部分定义的jobject类型的环境参数obj作为中介。
	//至此,JNI框 架固有的两个环境入参env和obj,我们都有涉及。
	for (; i < ARRAY_LENGTH; i++) {
		(*env)->SetObjectField(env, obj, topicFieldId, string);
		(*env)->SetObjectArrayElement(env, mails, i, obj);
	}
	return mails;
}
#ifdef __cplusplus
}
#endif

package com.ndk.test;

public class JniClient {
	static {
		System.loadLibrary("TestNdk");
	}

	private JniClient() {
	}

	private static JniClient jniClient = new JniClient();

	public static JniClient getInstance() {
		return jniClient;
	}

	public native String AddStr(String strA, String strB);

	public native int AddInt(int a, int b);

	// ==============
	public native int intArrayMethord(int[] intArray);

	public native boolean booleanMethord(boolean b);
}