在写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); }