JNI/NDK入门指南之Android下JNI开发

                                 Android下JNI开发

    对于开发Android下App的码农们来说jni是一个相对应用得比较少的玩意(相对于Android系统来说),正好最近这一阵子公司的项目比较轻松有一定的空闲时间,就利用起来整理了一下Android下Jni开发常用到的知识点和一些比较重要的用法,发话不多说直接进入主题。
    在这里,对于Jni的一些基本的介绍我就不多说了感兴趣的可以去问度娘或者谷歌,我在这里主要是以代码为主讲解关于Jni的主要用法,如果有什么遗漏或者错误的地方希望大家勿喷额。直接上代码来看看我定义的本地方法,域和方法名:
	private int x = 0;
	private int y = 0;
	private String name;
	public static final String TAG="NDK";
    /** Called when the activity is first created. */
    private Person person=null;
	
    
    
    private static final String DEBUGTAG = "NATIVE";
    private String myString;
    private static int si=0;
    private static String helloString = "What's your name";   
    //对基本数据类型的访问
    private native String GetNativeString(String str);//访问字串
    private native int SumArray(int [] ar, int length);//传一个数组的长度
    private native int SumArrayA(int [] ar);//不传长度
    private native int SumArray2D(int [][]ar2d);//传一个二维数组,返回一个长度    
    private native int [][] ModifyArray2d(int [][]ar2d);//传入一个二维数组,返回一个二维数组 
    //这一部分是对成员和方法的访问
    private native void AccessField();//访问静态成员
    private native void AccessMethod();     
    private native void signature(String str1, int i,byte b,char c,boolean bl,long l,double d,float f,short sh);
    
    
    
	// jni中注册的方法
    public native String HelloLoad();
    public native int add(int x, int y);  
    public native void change();//通过本地方法修改java属性
    public native void change_int();//通过落本地方法修改java蛇形
    public native void calljavamethod();

/* ***************** 定义本地方法 ******************* */
     // 输入常用的数值类型(Boolean,Byte,Char,Short,Int,Float,Double)
     public  native  void  displayParms(String showText,  int  i, boolean bl);
     // 调用一个静态方法
     //public  native  int  add( int  a,  int  b);
     // 输入一个数组
     public  native  void  setArray(boolean[] blList);
     // 返回一个字符串数组
     public  native String[] getStringArray();
     // 返回一个结构
     public  native DiskInfo getStruct();
     // 返回一个结构数组
     public  native DiskInfo[] getStructArray();
     
     // jni中注册的方法
     private native int getPersonInfoByIndex(Person person, int index);
     
     
     
     public static String sayHello(String name) {  
         return "Hello, " + name + "!";  
     }  
        
     public String sayHello() {  
         return "Hello, " + name + "!";  
     }  
     
     
     
     
     private void callback(String strFromNative,int i,byte b,char c,boolean bl,long l,double d,float f,short sh){
     	Log.d(DEBUGTAG,"CALL FROM NATIVE:"+strFromNative +i+b+c+bl+l+d+f+sh);       
     	System.out.println("CALL FROM NATIVE:"+strFromNative +i+b+c+bl+l+d+f+sh);
     }
     private  static String  callStaticBack(){
     	System.out.println("What's your name ");
     	return    "What's your name ";
     }

上面的代码就是定义的一些本地方法,域和相关方法名,下面来看看我是怎么实现对本地方法的调用同时怎么实现c语言调用java函数和修改java里面的域值,下面直接上代码:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <jni.h>
#include <assert.h>


// 获取数组的大小
# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
// 指定要注册的类,对应完整的java类名
#define JNIREG_CLASS "com/skywang/ndk/NdkLoad"
#define JNIPAR_CLASS "com/skywang/ndk/Person"
// 引入log头文件
#include <android/log.h>  

// log标签
#define  TAG    "hello_load"
// 定义info信息
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)
// 定义debug信息
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
// 定义error信息
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)



// 硬盘信息
struct    {
     char  name[ 256 ];
     int  serial;
} DiskInfo;

jstring nativeTojstring( JNIEnv* env,const char* str );
char* jstringToNative(JNIEnv *env, jstring jstr);
jstring stoJstring(JNIEnv* env, const char* pat)  ;


// jfieldID结构体,用于保存类“Person.java”的filedID
struct PersonOffsets
{
    jfieldID    name;
    jfieldID    age;
    jfieldID    height;
} gPersonOffsets;

// 与“Person.java”对应的结构体,用于保存数据,并将数据赋值给Person.java的成员
typedef struct tagPerson
{
    char    mName[10];
    int     mAge;
    float   mHeight;
}Person;

// 定义了3个Person
static Person gPersons[] = {
    {"skywang", 25, 175},
    {"eman"   , 30, 166},
    {"Dan"    , 51, 172},
};
#define GPERSON_NUM NELEM(gPersons)




// 返回字符串"hello load jni"
JNIEXPORT jstring JNICALL native_hello(JNIEnv *env, jclass clazz)
{
    return (*env)->NewStringUTF(env, "hello load jni.");
}



JNIEXPORT jint JNICALL native_add(JNIEnv *env, jclass clazz, jint x, jint y)
{
		return x+y;
}


//修改java中的String属性
JNIEXPORT void JNICALL native_change(JNIEnv *env, jobject this)
{
			jfieldID fid; 
			jclass cls;  
			// 先获得class对象  
            cls = (*env)->FindClass(env, JNIREG_CLASS);  
		      // 获取属性ID, 通过属性名和签名  
            fid = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");  
            if (fid != 0)  
            {  
                const char* name = "icejoywoo";  
                jstring arg = (*env)->NewStringUTF(env, name);  
                (*env)->SetObjectField(env, this, fid, arg); // 修改属性  
            }
}

//修改java中的int属性
JNIEXPORT void JNICALL native_change_int(JNIEnv *env, jobject this)
{
			jfieldID fid; 
			jclass cls;  
			// 先获得class对象  
            cls = (*env)->FindClass(env, JNIREG_CLASS);  
		      // 获取属性ID, 通过属性名和签名  
            fid = (*env)->GetFieldID(env, cls, "x", "I");  
            if (fid != 0)  
            {  
                jint arg = 5;  
                (*env)->SetIntField(env,this, fid, arg); // 修改属性  
            }
}


//修改java中的方法
JNIEXPORT void JNICALL native_calljavamethod(JNIEnv *env, jobject this)
{
			jfieldID fid; 
			jclass cls; 
			jmethodID mid; 
			// 先获得class对象  
            cls = (*env)->FindClass(env, JNIREG_CLASS);  

			// 获取方法ID, 通过方法名和签名, 调用静态方法  
			// 获取方法ID, 通过方法名和签名, 调用静态方法  
            mid = (*env)->GetStaticMethodID(env, cls, "sayHello", "(Ljava/lang/String;)Ljava/lang/String;");  
			if (mid != 0)  
            {  
                const char* name = "World";  
                jstring arg = (*env)->NewStringUTF(env, name);  
                jstring result = (jstring)(*env)->CallStaticObjectMethod(env, cls, mid, arg);  
                const char* str = (*env)->GetStringUTFChars(env, result, 0); 
				LOGI("%s info\n", __func__);
                LOGD("%s debug\n", __func__);
                LOGE("%s error\n", __func__);
				LOGI("Result of sayHello: %s\n\n", str);
                printf("Result of sayHello: %s\n", str);  
				(*env)->ReleaseStringUTFChars(env, result, str);
            }



			// 调用成员方法  
            mid = (*env)->GetMethodID(env, cls, "sayHello", "()Ljava/lang/String;");  
            if (mid != 0)  
            {  
                jstring result = (jstring)(*env)->CallObjectMethod(env, this, mid);  
                const char* str = (*env)->GetStringUTFChars(env, result, 0); 
				LOGI("Result of sayHello: %s\n\n", str);
                printf("Result of sayHello: %s\n", str);  
				(*env)->ReleaseStringUTFChars(env, result, str);
            }  
}



//输入常用的数值类型 Boolean,Byte,Char,Short,Int,Float,Double
JNIEXPORT void JNICALL native_displayParms(JNIEnv *env, jobject this, jstring s, jint i, jboolean b)
{
	const char* szStr = (*env)->GetStringUTFChars(env, s, 0 );
	LOGI( "String = [%s]\n", szStr );
    printf( "String = [%s]\n", szStr );
	LOGI( "int = %d\n", i );
    printf( "int = %d\n", i );
	LOGI("boolean = %s\n", (b==JNI_TRUE ? "true" : "false"));
    printf( "boolean = %s\n", (b==JNI_TRUE ? "true" : "false") );
    (*env)->ReleaseStringUTFChars(env, s, szStr );
}


///输入一个数组,这里输入的是一个Boolean类型的数组
JNIEXPORT void JNICALL native_setArray(JNIEnv *env, jobject this, jbooleanArray ba)
{
    jboolean* pba = (*env)->GetBooleanArrayElements(env,ba, 0 );
    jsize len = (*env)->GetArrayLength(env, ba);
    int i=0;
    // change even array elements
    for( i=0; i < len; i+=2 )
    {
        pba[i] = JNI_FALSE;
        printf( "boolean = %s\n", (pba[i]==JNI_TRUE ? "true" : "false") );
		LOGI( "boolean = %s\n", (pba[i]==JNI_TRUE ? "true" : "false") );
    }
    (*env)->ReleaseBooleanArrayElements(env, ba, pba, 0 );
}


返回一个字符串数组
JNIEXPORT jobjectArray JNICALL native_getStringArray(JNIEnv * env, jobject this)
{
	jstring str;
	jobjectArray args = 0;
	jsize len = 5;
	int   i=0;
	char * sa[]=  { "Hello,", "world!", "JNI", "is", "fun" };
	args = (*env)->NewObjectArray(env,len,(*env)->FindClass(env,"java/lang/String"),0);
    for( i=0; i < len; i++ )
    {
        str = (*env)->NewStringUTF(env, sa[i] );
        (*env)->SetObjectArrayElement(env, args, i, str);
    }
    return args;
}



/*
 * Class:     com_skywang_ndk_NdkLoad
 * Method:    getStruct
 * Signature: ()Lcom/skywang/ndk/DiskInfo;
 */
 
//返回一个结构,这里返回一个硬盘信息的简单结构类型
JNIEXPORT jobject JNICALL native_getStruct(JNIEnv *env , jobject this)
{
	 /* 下面为获取到Java中对应的实例类中的变量*/

    //获取Java中的实例类
    jclass objectClass = (*env)->FindClass(env,"com/skywang/ndk/DiskInfo");
	jmethodID mid = (*env)->GetMethodID(env, objectClass, "<init>","()V");  
    jobject obj   = (*env)->NewObject(env, objectClass, mid); 

    //获取类中每一个变量的定义
    //名字
    jfieldID str = (*env)->GetFieldID(env,objectClass,"name","Ljava/lang/String;");
	if(str != 0)
	{
		LOGI("str !=0 \n");
	}

    //序列号
    jfieldID ival = (*env)->GetFieldID(env,objectClass,"serial","I");

	if(ival != 0)
	{
		LOGI("ivals !=0 \n");
	}


    //给每一个实例的变量付值
    (*env)->SetObjectField(env, obj, str, (*env)->NewStringUTF(env,"my name is D:"));
    (*env)->SetIntField(env, obj, ival, 10);
    
    return obj;
}




/*
 * Class:     com_skywang_ndk_NdkLoad
 * Method:    getStructArray
 * Signature: ()[Lcom/skywang/ndk/DiskInfo;
 */
//返回一个结构数组,返回一个硬盘信息的结构数组
JNIEXPORT jobjectArray JNICALL native_getStructArray(JNIEnv *env, jobject this)
{
	//申明一个object数组 
    jobjectArray args = 0;
    
    //数组大小
    jsize        len = 5;

    //获取object所属类,一般为ava/lang/Object就可以了
    jclass objClass = (*env)->FindClass(env, "java/lang/Object");

    //新建object数组
    args = (*env)->NewObjectArray(env, len, objClass, 0);

    /* 下面为获取到Java中对应的实例类中的变量*/

    //获取Java中的实例类
    jclass objectClass = (*env)->FindClass(env, "com/skywang/ndk/DiskInfo");
    jmethodID mid = (*env)->GetMethodID(env, objectClass, "<init>","()V");  
    jobject obj   = (*env)->NewObject(env, objectClass, mid); 


    
    //获取类中每一个变量的定义
    //名字
    jfieldID str = (*env)->GetFieldID(env, objectClass,"name","Ljava/lang/String;");
    //序列号
    jfieldID ival = (*env)->GetFieldID(env, objectClass,"serial","I");

    //给每一个实例的变量付值,并且将实例作为一个object,添加到objcet数组中
	int i = 0;
    for(i=0; i < len; i++ )
    {
        //给每一个实例的变量付值
        jstring jstr = nativeTojstring(env,"我的磁盘名字是 D:");
		//jstring jstr = stoJstring(env,"我的磁盘名字是 D:");
        //(env)->SetObjectField(_obj,str,(env)->NewStringUTF("my name is D:"));
        (*env)->SetObjectField(env, obj, str, jstr);
        (*env)->SetIntField(env, obj, ival, 10);

        //添加到objcet数组中
        (*env)->SetObjectArrayElement(env, args, i, obj);
    }
    //返回object数组
    return args;

}


/* 
 * 根据index获取Person信息。
 * 参数说明:
 *         env : JNI 接口指针。
 *        claszz : Java 类对象。
 *        person : 输入参数,java对象
 *        index : 输入参数,序列号。
 */
JNIEXPORT jint JNICALL 
getPersonInfoByIndex(JNIEnv *env, jobject clazz, jobject person, jint index)
{
	//若index无效,则直接返回-1
	if((int)index < 0 || (int)index>=GPERSON_NUM)
		return -1;

	// 将Person数组(gPersons)中的第index个成员赋值给pPerson指针
    Person *pPerson = &gPersons[index];

	//设置java对象person的nName
	jstring name = (*env)->NewStringUTF(env, pPerson->mName);
	(*env)->SetObjectField(env, person, gPersonOffsets.name, name);

    // 设置java对象person的mAge
    (*env)->SetIntField(env, person, gPersonOffsets.age, pPerson->mAge);
    // 设置java对象person的mHeight
    (*env)->SetFloatField(env, person, gPersonOffsets.height, pPerson->mHeight);

    LOGD("%s index-%d  mName:%s, mAge:%d, mHeight:%f\n", 
            __func__, index, pPerson->mName, pPerson->mAge, pPerson->mHeight);

    return 0;
}


/*
 * Class:     com_skywang_ndk_NdkLoad
 * Method:    GetNativeString
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
jstring JNICALL native_GetNativeString(JNIEnv *env, jobject obj, jstring str) 
 {
	const char * pStr;
	const char * pUTF8Str = "From jni String";
	char outbuff[128] = {0};


	//获取传入字串中的所有字串
    pStr = (*env)->GetStringUTFChars(env, str, NULL);
    if (pStr == NULL)
       return NULL;
    strcat(outbuff, pStr);
    (*env)->ReleaseStringUTFChars(env, str, pStr); //这里要释放,否则内存泄漏
    strcat(outbuff, "/"); //加一个分隔符
    //从传入字串第三个位置开始,获取4个,到字串尾
    (*env)->GetStringUTFRegion(env, str, 3, 4, outbuff + strlen(outbuff));
    strcat(outbuff, "/");    //加一个分隔符
    strcat(outbuff, pUTF8Str);    //联接一个本地字串
    //从buff中构建一个jstring返回给java,这个新构建的字串由虚拟机负责回收
    return (*env)->NewStringUTF(env, outbuff);
 }



/*
 * Class:     com_skywang_ndk_NdkLoad
 * Method:    SumArray
 * Signature: ([II)I
 */
JNIEXPORT jint JNICALL native_SumArray(JNIEnv * env, jobject obj, jintArray arr, jint length)
{
	int i, sum = 0;
	jint *pOutbuf = NULL;
	if(length > 0)
         pOutbuf = (jint*) malloc(length * sizeof(jint));//开辟内存
    else
         return 0;

	(*env)->GetIntArrayRegion(env, arr, 0, length, pOutbuf);//获取int数组
	for(i = 0; i < 10; i++)
	{
		sum += pOutbuf[i];
	}
	free(pOutbuf);
	return sum;
}



/*
 * Class:     com_skywang_ndk_NdkLoad
 * Method:    SumArrayA
 * Signature: ([I)I
 */
 /*
 *
 *    ReleaseIntArrayElements
 *        第三个参数就是决定更新与否的。
 *             取值 零(0) 时,更新数组并释放所有元素;
 *             取值 JNI_COMMIT 时,更新但不释放所有元素;
 *             取值 JNI_ABORT 时,不作更新但释放所有元素;
 *      #define JNI_COMMIT 1 // copy content, do not free buffer
 *      #define JNI_ABORT  2 // free buffer w/o copying back ^/
 *         //获取数组长度,注意jarray是指所有类型j<类型>array
 *         typedef jarray jintArray //其他的jbyteArray或jfloatArray都是jarray.
 *       jsize       (*GetArrayLength)(JNIEnv*, jarray);
 */
JNIEXPORT jint JNICALL native_SumArrayA(JNIEnv * env, jobject obj, jintArray arr)
{
	int i, j, sum = 0;
	jint* buf;
	j = (*env)->GetArrayLength(env, arr);
	//另外,这里返回的值,是jint*,不是const jint*,也就是说可以对其中的值进行修改
    buf = (*env)->GetIntArrayElements(env, arr, NULL);//这一句其实也告java内存回收器,不要回收arr数组的内存,或者不要整理内存
                                                      //如果回收器,回收和整理内存,那么我们在c中,访问的地址就可能错了
	for(i = 0; i < j; i++)//这里是求和
	{
		sum += buf[i];
	}

	//现在我们来修改一下buf中的元素,看看返回后,会不会把buf中的值,更新到java中的arr里去
	//经过验证修改是成功的
    buf[0] = 100;
    buf[1] = 200;

    (*env)->ReleaseIntArrayElements(env, arr, buf, 0);//调用这个方法,告诉java内存回收器,我忆用完了arr,,现在可以回收arr的内存了
    return sum;

}


/*
 * Class:     com_skywang_ndk_NdkLoad
 * Method:    AccessField
 * Signature: ()V
 */
 /**
 *    访问java中的成员[通用的逻辑]
 *    注意:访问的是java中非静态成员,非静态成员是属于对象的.另外还要访问静态成员,静态成员属于类
 *    步骤:
 *    1:先找到对象的类
 *    2:根据成员的名称和类型,获取该成员的id引用
 *    3:根据id引用,获取该成员的域引用
 *    4:根据域引用再得到具体的内容
 *
 */
JNIEXPORT void JNICALL native_AccessField(JNIEnv *env, jobject obj)
{
	jclass cls;//void* 指针
	jfieldID fid;//抽象的结构体指针
    jstring jstr;//void*指针
    jint value;
    const char* str;

	cls = (*env)->GetObjectClass(env,obj);//得到类的引用
	fid = (*env)->GetFieldID(env, cls, "myString", "Ljava/lang/String;");//得到成员id的引用

	if( fid == NULL ){//检测根据给定的信息是否找到了id,
        return ;
    }

	jstr = (*env)->GetObjectField(env, obj, fid);//得到对象的引用
    str = (*env)->GetStringUTFChars(env, jstr, NULL);//从对象引用中得到内容
    if( str == NULL ){
        return ;
    }
	
	//printf(" c.s = "%s"\n", str ) ;
	LOGI(" c.s = %s\n", str );
	(*env)->ReleaseStringUTFChars(env, jstr, str);//更新或释放,不过最好用SetObjectField来操作。
	
	jstr = (*env)->NewStringUTF(env, "accedfild");//创建一个新的字串,这个是用java的方法创建的
    if( jstr == NULL ){
        return ;
    }
    //用jstr,更新obj对象的fid域.
    (*env)->SetObjectField(env, obj, fid, jstr);//设置域对象



	    //===============访问int静态成员=======================
    fid = (*env)->GetStaticFieldID(env,cls,"si","I");
    if(NULL == fid)
        return;
    value = (*env)->GetStaticIntField(env, cls, fid);//注意第二个参数是类,不是对象,因为是静态成员
    value = 100;
    (*env)->SetStaticIntField(env, cls, fid, 100);
    //===============访问String静态成员=======================
    fid = (*env)->GetStaticFieldID(env, cls, "helloString", "Ljava/lang/String;");
    jstr = (*env)->GetStaticObjectField(env, cls, fid);//注意第二个参数是类,不是对象,因为是静态成员
    str = (*env)->GetStringUTFChars(env, jstr, NULL);
    //可以对str做一些处理
    (*env)->ReleaseStringUTFChars(env, jstr, str);
    jstr = (*env)->NewStringUTF(env, "static string");
    (*env)->SetStaticObjectField(env, cls, fid, jstr);//注意第二个参数是类,不是对象,因为是静态成员

    return;
}


/*
 * Class:     com_skywang_ndk_NdkLoad
 * Method:    AccessMethod
 * Signature: ()V
 */
 /*
 * 访问java中的方法
 * 并传递了所有java中的数据类型,对象类型除外
 *
 *
 */
JNIEXPORT void JNICALL native_AccessMethod(JNIEnv *env, jobject obj)
{
	   jclass cls;
    jmethodID mid;
    const char  *str = "hello call back";
    jstring jstr;

    jboolean    z = 1;
    jbyte       b = 0xff;
    jchar       c = 'c';
    jshort      s = 60;
    jint        i = 100;
    jlong       j = 568978523;
    jfloat      f = 125643.22222;
    jdouble     d = 123456789.12654789;
    //jobject     l = ;

    cls = (*env)->GetObjectClass(env,obj);//得到类的引用

    //从静态方法中得到str,再传回给callback方法
    mid = (*env)->GetStaticMethodID(env, cls,"callStaticBack","()Ljava/lang/String;");
    jstr = (*env)->CallStaticObjectMethod(env,cls,mid);

    mid = (*env)->GetMethodID(env, cls, "callback", "(Ljava/lang/String;IBCZJDFS)V");

    if (mid == NULL) {
        return; /* method not found */
    }
    //printf("In C\n");

    //jstr = (*env)->NewStringUTF(env, str);
    (*env)->CallVoidMethod(env, obj, mid,jstr,i,b,c,z,j,d,f,s);
}

 
// Java和JNI函数的绑定表
static JNINativeMethod method_table[] = {
    { "HelloLoad", "()Ljava/lang/String;", (void*)native_hello },//绑定
	{"add", "(II)I", (void*)native_add},
	{"change", "()V", (void*)native_change},
	{"change_int", "()V", (void*)native_change_int},
	{"calljavamethod", "()V", (void*)native_calljavamethod},
	{"displayParms", "(Ljava/lang/String;IZ)V", (void*)native_displayParms},
	{"setArray", "([Z)V", (void*)native_setArray},
	{"getStringArray", "()[Ljava/lang/String;", (void*)native_getStringArray},
	{"getStruct", "()Lcom/skywang/ndk/DiskInfo;", (void*)native_getStruct},
	{"getStructArray", "()[Lcom/skywang/ndk/DiskInfo;", (void*)native_getStructArray},
	{"getPersonInfoByIndex", "(Lcom/skywang/ndk/Person;I)I", (void*)getPersonInfoByIndex },//绑定
	{"GetNativeString", "(Ljava/lang/String;)Ljava/lang/String;", (void*)native_GetNativeString},//绑定
    {"SumArray", "([II)I", (void*)native_SumArray},//绑定
	{"SumArrayA", "([I)I", (void*)native_SumArrayA},//绑定
	{"AccessField", "()V", (void*)native_AccessField},//绑定
	{"AccessMethod", "()V", (void*)native_AccessMethod},//绑定
};






char* jstringToNative(JNIEnv *env, jstring jstr)
{
	if ((*env)->ExceptionCheck(env) == JNI_TRUE || jstr == NULL)
	{
		(*env)->ExceptionDescribe(env);
		(*env)->ExceptionClear(env);
		printf("jstringToNative函数转换时,传入的参数str为空");
		return NULL;
	} 

	jbyteArray bytes = 0; 
	jthrowable exc; 
	char *result = 0; 
	if ((*env)->EnsureLocalCapacity(env,2) < 0) 
	{ 
		return 0; /* out of memory error */ 
	} 
	jclass jcls_str = (*env)->FindClass(env,"java/lang/String"); 
	jmethodID MID_String_getBytes = (*env)->GetMethodID(env,jcls_str, "getBytes", "()[B"); 

	bytes = (jbyteArray)(*env)->CallObjectMethod(env,jstr, MID_String_getBytes); 
	exc = (*env)->ExceptionOccurred(env); 
	if (!exc) 
	{ 
		jint len = (*env)->GetArrayLength(env, bytes); 
		result = (char *)malloc(len + 1); 
		if (result == 0) 
		{ 
			//JNU_ThrowByName(env, "java/lang/OutOfMemoryError", 	0); 
			(*env)->DeleteLocalRef(env,bytes); 
			return 0; 
		} 
		(*env)->GetByteArrayRegion(env,bytes, 0, len, (jbyte *)result); 
		result[len] = 0; /* NULL-terminate */ 
	} 
	else 
	{ 
		(*env)->DeleteLocalRef(env, exc); 
	} 
	(*env)->DeleteLocalRef(env, bytes); 
	return (char*)result; 
} 


jstring nativeTojstring( JNIEnv* env,const char* str )
{
	#if 0
	jclass strClass = (*env)->FindClass(env, "java/lang/String"); 
	jmethodID ctorID = (*env)->GetMethodID(env, strClass, "<init>", 
		"([BLjava/lang/String;)V"); 

	if ((*env)->ExceptionCheck(env) == JNI_TRUE || str == NULL)
	{
		(*env)->ExceptionDescribe(env);
		(*env)->ExceptionClear(env);
		printf("nativeTojstring函数转换时,str为空/n");
		return NULL;
	} 

	jbyteArray bytes = (*env)->NewByteArray(env, strlen(str)); 
	//如果str为空则抛出异常给jvm

	(*env)->SetByteArrayRegion(env, bytes, 0,  strlen(str), (jbyte*)str); 
	//jstring encoding = env->NewStringUTF( "GBK"); 
	jstring encoding = (*env)->NewStringUTF(env, "UTF8"); 
	jstring strRtn = (jstring)(*env)->NewObject(env, strClass, ctorID, bytes, encoding);
	//释放str内存
	// free(str);
	return strRtn;
	#endif

			//定义java String类 strClass
	jclass strClass = (*env)->FindClass(env,"java/lang/String");
	//获取java String类方法String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String
	jmethodID ctorID = (*env)->GetMethodID(env, strClass, "<init>", "([BLjava/lang/String;)V");
	//建立byte数组
	jbyteArray bytes = (*env)->NewByteArray(env, (jsize)strlen(str));
	//将char* 转换为byte数组
	(*env)->SetByteArrayRegion(env, bytes, 0, (jsize)strlen(str), (jbyte*)str);
	//设置String, 保存语言类型,用于byte数组转换至String时的参数
	jstring encoding = (*env)->NewStringUTF(env, "utf-8"); 
	//将byte数组转换为java String,并输出
	return (jstring)(*env)->NewObject(env, strClass, ctorID, bytes, encoding);

}


//char* to jstring   
jstring stoJstring(JNIEnv* env, const char* pat)   
{   
       jclass strClass = (*env)->FindClass(env, "java/lang/String");   
       jmethodID ctorID = (*env)->GetMethodID(env, strClass, "<init>", "([BLjava/lang/String;)V");   
       jbyteArray bytes = (*env)->NewByteArray(env, strlen(pat));   
       (*env)->SetByteArrayRegion(env, bytes, 0, strlen(pat), (jbyte*)pat);   
       jstring encoding = (*env)->NewStringUTF(env, "utf-8");   
       return (jstring)(*env)->NewObject(env, strClass, ctorID, bytes, encoding);   
}  



// 注册native方法到java中
static int registerNativeMethods(JNIEnv* env, const char* className,
        JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;
    clazz = (*env)->FindClass(env, className);
    if (clazz == NULL) {
        return JNI_FALSE;
    }
    if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
        return JNI_FALSE;
    }

    return JNI_TRUE;
}


// 初始化函数,用于获取Java中各个成员对应的fieldID。
static void nativeClassInit (JNIEnv *env)
{   
    jclass personClass = (*env)->FindClass(env, JNIPAR_CLASS);       
    // 获取Person的mName成员对应的FieldID,并保存到gPersonOffsets中
    gPersonOffsets.name     = (*env)->GetFieldID(env, personClass, "mName"  , "Ljava/lang/String;");        
    // 获取Person的mAge成员对应的FieldID,并保存到gPersonOffsets中
    gPersonOffsets.age      = (*env)->GetFieldID(env, personClass, "mAge"   , "I");
    // 获取Person的mHeight成员对应的FieldID,并保存到gPersonOffsets中
    gPersonOffsets.height   = (*env)->GetFieldID(env, personClass, "mHeight", "F");
}


int register_ndk_load(JNIEnv *env)
{
    nativeClassInit(env);

    // 调用注册方法
    return registerNativeMethods(env, JNIREG_CLASS,
            method_table, NELEM(method_table));
}



JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1; 

    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return result;
    }   

    register_ndk_load(env);

    // 返回jni的版本
    return JNI_VERSION_1_4;
}

    上面的代码我已经添加了比较详细的代码注释了,只要有一定的jni基础的就能看懂了,这些代码片段是我整理网上的一些相关片段同时我一一运行验证过了,并且加上了一些自己的注释。jni基本的和重要的一些用法大概就这些了,如果大家想更加详细的了解jni编程可以详细的看看jni编程指南,它比较详细的讲解了jni的相关知识。希望上述能对大家学习jni有帮助。
      下面附上Demo
 

猜你喜欢

转载自blog.csdn.net/tkwxty/article/details/42062467