ndk 开发学习2-jni中 c++和java之间的一些交互细节

本文承接之前的ndk开发1文章http://www.jianshu.com/p/58165ce16a72

这次的主要内容是讲java中的基本数据类型是怎么和c++文件交互的:

首先,我们扩展Leitest这个类,新增若干个native方法,这些native方法具体验证的东西是不一样的,接下来会细讲

ndk 开发学习2-jni中 c++和java之间的一些交互细节_第1张图片
Leitest类

详细代码如下

package com.example.lei.testa;
 
import android.content.Context;
import android.widget.Toast;
 
/**
 * Created by lei on 17-1-11.
 */
 
public class Leitest {
 
 
   public static int statictest=1;
   private final Context appContext;
   static {
      System.loadLibrary("hello");
   }
   public Leitest (Context context){
      appContext=context.getApplicationContext();
   }
 
   private void toasttest(){
      Toast.makeText(appContext,"toast",Toast.LENGTH_LONG).show();
   }
 
   public String getTestvalue() {
      return testvalue;
   }
 
   public void setTestvalue(String testvalue) {
      this.testvalue = testvalue;
   }
 
   private String testvalue="abc";
 
 
   //public  native String getStringFromC();
   public static native String transLateFrom(String strIn);
   public static native int sumArray(int[] array);
 
   public static native int[][]get2DArray(int size);
 
   public  native void changeFiled();
 
   public static native void staticChange();
   public native void callToast();
   public native String creatNewStringByBytes(byte[]bytes);
}

第一个transLateFrom(String strIn)方法,就是将一个字符串通过c++进行大写转换,然后返回大写化的字符串,详细代码如下

JNIEXPORT jstring JNICALL Java_com_example_lei_testa_Leitest_transLateFrom
        (JNIEnv * env, jclass class1, jstring obj){
    const char* str= env->GetStringUTFChars(obj,NULL);
    env->ReleaseStringUTFChars(obj,str);
    std::string strtemp=str;
    for (auto it = strtemp.begin();it!=strtemp.end()  ; ++it) {
        *it=towupper(*it);
    }
    return env->NewStringUTF(strtemp.c_str());
}

env 就是指向jni功能函数库的指针,利用 env->GetStringUTFChars(obj,NULL) 就可以将java传入的String类型(即 c++中的jstring)转换为c++里面的标准 const char* 类型的字符串,在利用env->NewStringUTF(const char*) 就可以将c++的字符串转回java的字符串(jstring)了

第二个sumArray(int[] array)函数的功能是传入一int类型的数组,然后用c++求和,返回结果,实现代码如下

JNIEXPORT jint JNICALL Java_com_example_lei_testa_Leitest_sumArray
        (JNIEnv * env, jclass class1, jintArray array){
    const int size= env->GetArrayLength(array);
    jint *arraysum;
    jint sum=0,i=0;
    arraysum= env->GetIntArrayElements(array,NULL);
    for ( ; i ReleaseIntArrayElements(array,arraysum,0);
    return sum;
}

env->GetArrayLength(array) 获取传入的java数组的长度,利用env->GetIntArrayElements(array,NULL)方法得到jint(java int)型的数组(也可以认为是指向这个数组第一个元素的指针),然后遍历求和,注意为了避免内存泄露的发生,尽量释放掉内存,如env->ReleaseIntArrayElements(array,arraysum,0);

第三个 int[][]get2DArray(int size); 返回一个二维数组,主要是验证c++返回一个非基本类型的数组是怎么实现的,具体代码如下

JNIEXPORT jobjectArray JNICALL Java_com_example_lei_testa_Leitest_get2DArray
        (JNIEnv * env, jclass jclass1, jint size){
  jclass intarrayclass= env->FindClass("[I");
    if (intarrayclass==NULL) return NULL;

  jobjectArray result=  env->NewObjectArray(size,intarrayclass,NULL);
    if (result==NULL) return NULL;
    for(int i=0;iNewIntArray(size);
        env->SetIntArrayRegion(outjarrayInt,0,size,arryrow);
       env->SetObjectArrayElement(result,i,outjarrayInt);
    }
    return result;
}

jclass intarrayclass= env->FindClass("[I"),JNI 函数 FindClass 来获得一个 int 型二维数组类的引用,传递给 FindClass 的参数“[I”是 JNI class descriptor(JNI 类型描述符),它对应着 java 中的 int[]类型。
然后利用env->NewObjectArray(size,intarrayclass,NULL)创建对象数组int[][],
jintArray outjarrayInt = env->NewIntArray(size) 创建临时的jint类型数组,
env->SetIntArrayRegion(outjarrayInt,0,size,arryrow) 给临时jint数组赋值,
env->SetObjectArrayElement(result,i,outjarrayInt) 将int[]做为一个元素塞入 int[][]中

第四个public native void changeFiled()和第五个 public static native void staticChange()功能上有些类似,一个是改变一个对象的成员的field的值,一个是改变一个类 static field的值,不多说,贴代码

JNIEXPORT void JNICALL Java_com_example_lei_testa_Leitest_changeFiled
        (JNIEnv *env, jobject obj){
  jclass testclass= env->GetObjectClass(obj);
   jfieldID  testfiled=env->GetFieldID(testclass,"testvalue","Ljava/lang/String;");
    jstring  jstring1=env->NewStringUTF("kkkk");
    env->SetObjectField(obj,testfiled,jstring1);
}

JNIEXPORT void JNICALL Java_com_example_lei_testa_Leitest_staticChange
        (JNIEnv * env, jclass cs){
   jfieldID sfiled=  env->GetStaticFieldID(cs,"statictest","I");
    env->SetStaticIntField(cs,sfiled,23);
}

都是三步
1.类查找 ;
2.通过类找到field iD
3.通过fieldid修改值
需要注意的是 jfieldID testfiled=env->GetFieldID(testclass,"testvalue","Ljava/lang/String;");中的"Ljava/lang/String;"和 jfieldID sfiled= env->GetStaticFieldID(cs,"statictest","I");中的“I” 也是JNI 类型描述符,指的就是这个field的类型

第六个 public native void callToast();是用来调用类的成员函数的,代码如下

JNIEXPORT void JNICALL Java_com_example_lei_testa_Leitest_callToast
        (JNIEnv *env, jobject oj){
    jclass  cs=env->GetObjectClass(oj);
    jmethodID  calltoast=env->GetMethodID(cs,"toasttest","()V");
    env->CallVoidMethod(oj,calltoast);
}

同样是三步
1.类查找 ;
2.通过类找到Method iD
3.通过Method iD调用
同样注意 jmethodID calltoast=env->GetMethodID(cs,"toasttest","()V");其中“toasttest”是java中的函数名,这个见leitest的代码,"()V"也是JNI 类型描述符,表示没有参数 返回void

第七个 public native String creatNewStringByBytes(byte[]bytes)测试的是调用构造方法新建对象,功能是将byte[]转为String类型,即String的byte[]参数的构造方法测试

代码如下

JNIEXPORT jstring JNICALL Java_com_example_lei_testa_Leitest_creatNewStringByBytes
        (JNIEnv * env, jobject oj, jbyteArray bytesarray){
    jclass stringclass=env->FindClass("java/lang/String");
    jmethodID methodId=env->GetMethodID(stringclass,"","([B)V");
    jobject str=env->NewObject(stringclass,methodId,bytesarray);
    env->DeleteLocalRef(stringclass);
    return (jstring) str;
}

补上一个返回数组的方法,被setArrayRegion坑出翔了

JNIEXPORT jintArray JNICALL Java_com_test_git_jnidemo_JniUtil_JniDemo_createArrayMethod
        (JNIEnv *env, jobject jobj, jint len){
    //1.新建长度len数组
    jintArray jarr = env->NewIntArray(len);
    //2.获取数组指针
    jint *arr = env->GetIntArrayElements(jarr, NULL);
    //3.赋值
    int i = 0;
    for(; i < len; i++){
        arr[i] = i;
    }
    //4.释放资源
    env->ReleaseIntArrayElements(jarr, arr, 0);
    //5.返回数组
    return jarr;
};

还是三步走
1.类查找 ;
2.通过类找到构造函数
3.通过NewObject调用
jmethodID methodId=env->GetMethodID(stringclass,"","([B)V");中 “”指构造函数,"([B)V"指参数为byte[],返回为空,JNI 类型描述符。

总结

以上代码都亲测可用,开发版本为AndroidStudio2.2,配置方案请回顾上一节的内容http://www.jianshu.com/p/58165ce16a72

你可能感兴趣的:(ndk 开发学习2-jni中 c++和java之间的一些交互细节)