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

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

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

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

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 <size ; ++i) {
        sum+=arraysum[i];
    }
    env->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;i<size;i++){
        jint arryrow[size];
        for (int j = 0; j < size; ++j) {
            arryrow[j]=i*size+j;
        }
       jintArray outjarrayInt = env->NewIntArray(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,"<init>","([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,"<init>","([B)V");中 “<init>”指构造函数,"([B)V"指参数为byte[],返回为空,JNI 类型描述符。

总结

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容