Android Studio下JNI编程2(接上篇,详细讲解java与c层互传参数)

Android Studio下JNI环境搭建、编译、运行等可以参考:

http://blog.csdn.net/huangximin1990/article/details/50441400


上篇文章讲述的JNI示例程序主要涉及两个文件:

NativeKit.java

cn_com_losy_jnitest_jni_NativeKit.cpp


注:由于字体调整关系,下列所示代码多了很多<span></span>标签。需要源码的同学可直接到git下载

https://git.oschina.net/huangximin/JniTest-AS.git


NativeKit.java文件(各方法功能注释写得很清楚,不做赘述)

/**
 * Created by huangximin on 2015/12/24.
 * 说明:本类中以Java层为基本视角
 *       Java层向native层传参称为传入
 *       native层向Java层传参称为传出
 */
public class NativeKit {
    private String name = "Java";

    // 回调方法.native层的doCallback方法会调用本方法
    public void callbackForJni(String fromNative) {
        Log.d("jni", "jni string from native:" + fromNative);
    }

    // 加载so文件
    static {
        System.loadLibrary("JniDemo");
    }

    // 求平方.分别测试int/float/double/long的传入和传出
    public native int square(int num);
    public native float square(float num);
    public native double square(double num);
    public native long square(long num);
    // 欢迎词.测试String的传入和传出
    public native String greetings(String username);
    // 获取二维数组.dimon*dimon的int型数组
    public native int[][] getTwoArray(int dimon);
    // 设置name.测试native层直接修改Java类成员变量
    public native void nativeSetName();
    // 回调.这个方法有点绕.native层的doCallback方法会再调用Java层(本类)的callbackForJni方法
    public native void doCallback();
    // 获取User实例.测试native层创建对象,然后返回给Java层
    public native User nativeGetUser();
    // native层获取Java层对象,并获取成员变量
    public native void printUserInfoAtNative(User user);
    // 修改User对象成员变量.同时测试Java层对象的传入、修改、传出
    public native User changeUserInfo(User user);
    // 获取对象列表.测试native层创建Java层对象列表,并返回
    public native ArrayList<User> nativeGetUserList(int num);

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


cn_com_losy_jnitest_jni_NativeKit.cpp文件的内容比较多,按照NativeKit类中方法声明顺序逐个看。

<span style="font-size:18px;color:#ff0000;">public native int square(int num);</span>
方法对应以下述方法:

<span style="font-size:18px;">JNIEXPORT jint JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_square__I
        (JNIEnv * env, jobject obj, jint num) {
    return num * num;
}</span>
其中,

JNIEXPORT 和JNICALL为JNI关键字,不用管它们

jint   对应int (具体数据类型对应详见本文最后)

Java_cn_com_losy_jnitest_jni_NativeKit_square__I 指明对应的方法

cn.com.losy.jnitest.jni包中的NativeKit类中的square方法

注:由于该类中square方法存在重载现象,而C语言没有重载,JNI的处理方法是在上述方法名最后面增加了

__I  (后面的I表示参数为一个int
字样以区分不同的方法

JNIEnv * env   JNIEnv 是JNI核心,有众多用处,后面会看到

jobject  obj    这个jobject需要两种情况分析。上段代码中square方法是一个非静态方法,在Java中要想调用它必须先实例化对象,然后再用对象调用它,那这个时候jobject就可以看做Java类的一个实例化对象,也就是obj就是一个NativeKit实例。如果square是一个静态方法,那么在Java中,它不是属于一个对象的,而是属于一个类的,Java中用NativeKit.square()这样的方式来调用,这个时候jobject就可以看做是java类的本身,也就是obj就是NativeKit.class。

jint num  即NativeKit中square(int num)方法传下来的参数

---------------------------------------

    public native float square(float num);
    public native double square(double num);
    public native long square(long num);
这三个方法与第一个方法类似,不做赘述

----------------------------------------

public native String greetings(String username);
对应

JNIEXPORT jstring JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_greetings
        (JNIEnv * env, jobject obj, jstring jstr) {
    // 读取来自Java层的string
    const char* str = <span style="color:#33cc00;">env->GetStringUTFChars(jstr, false);</span>
    if(str == NULL) {
        return NULL; /* OutOfMemoryError already thrown */
    }
    // 显示string
    std::printf("%s", str);
    std::printf("\n");
    // 释放资源
    <span style="color:#33cc00;">env->ReleaseStringUTFChars(jstr, str);</span>
    // 返回新string
    const char* tmpstr = "I am from Jni";
    <span style="color:#33cc00;">jstring rtstr = env->NewStringUTF(tmpstr);</span>
    return rtstr;
}
其中,值得注意的是读取String需

    const char* str = env->GetStringUTFChars(jstr, false);
构造String需

    const char* tmpstr = "I am from Jni";
    jstring rtstr = env->NewStringUTF(tmpstr);
---------------------------------------

public native int[][] getTwoArray(int dimon);
对应

JNIEXPORT jobjectArray JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_getTwoArray
        (JNIEnv * env, jobject obj, jint dimion) {
    if (dimion > 10) { // 假设 dimion  <= 10 ;
        return NULL;
    }
    // 获得一维数组 的类引用,即jintArray类型
    jclass intArrayClass = <span style="color:#009900;">env->FindClass("[I");</span>
    // 构造一个指向jintArray类一维数组的对象数组,该对象数组初始大小为dimion
    jobjectArray obejctIntArray = <span style="color:#009900;">env->NewObjectArray(dimion, intArrayClass, NULL);</span>

    // 构建dimion个一维数组,并且将其引用赋值给obejctIntArray对象数组
    for( int i = 0; i< dimion; i++ ) {
        // 构建jint型一维数组
        jintArray intArray = <span style="color:#33cc00;">env->NewIntArray(dimion);</span>
        // 设置jint型一维数组的值
        jint temp[10];  // 初始化一个容器,假设 dimion  <= 10 ;
        for( int j = 0; j < dimion; j++) {
            temp[j] = i + j; //赋值
        }
        // 将temp数组复制到intArray
        <span style="color:#33cc00;">env->SetIntArrayRegion(intArray, 0, dimion, temp);</span>
        // 给object对象数组赋值,即保持对jint一维数组的引用
        <span style="color:#33cc00;">env->SetObjectArrayElement(obejctIntArray, i, intArray);</span>
        // 删除局部引用
        <span style="color:#33cc00;">env->DeleteLocalRef(intArray);</span>
    }

    return  obejctIntArray; // 返回该对象数组
}
-------------------------------------

public native void nativeSetName();
对应

JNIEXPORT void JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_nativeSetName
        (JNIEnv * env, jobject obj) {
    jclass cls = <span style="color:#33cc00;">env->GetObjectClass(obj);</span> //获得Java层该对象实例的类引用,即NativeKit类引用

    jfieldID nameFieldId = <span style="color:#33cc00;">env->GetFieldID(cls, "name", "Ljava/lang/String;");</span> //获得属性句柄

    if(nameFieldId == NULL) {
        std::printf(" 没有得到name 的句柄Id \n;");
    }
    jstring javaNameStr = <span style="color:#33cc00;">(jstring) env->GetObjectField(obj ,nameFieldId);</span> // 获得该属性的值
    const char * c_javaName = <span style="color:#33cc00;">env->GetStringUTFChars(javaNameStr , NULL);</span> //转换为 char *类型

    std::printf("%s", c_javaName); //输出显示
    std::printf("\n");

    <span style="color:#33cc00;">env->ReleaseStringUTFChars(javaNameStr , c_javaName);</span>  //释放局部引用

    // 构造一个jString对象
    const char * c_ptr_name = "setByNative";
    jstring cName = <span style="color:#33cc00;">env->NewStringUTF(c_ptr_name);</span>

    <span style="color:#33cc00;">env->SetObjectField(obj, nameFieldId, cName);</span> // 设置该字段的值
}
-------------------------------------------

public native void doCallback();
对应

JNIEXPORT void JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_doCallback
        (JNIEnv * env, jobject obj) {
    jclass cls = <span style="color:#33cc00;">env->GetObjectClass(obj);</span> // 获得NativeKit类引用
    jmethodID callbackMethodID = <span style="color:#33cc00;">env->GetMethodID(cls, "callbackForJni", "(Ljava/lang/String;)V");</span> // 获得该回调方法句柄

    if(callbackMethodID == NULL) {
        std::printf("doCallback getMethodId is failed \n");
    }

    jstring native_desc = <span style="color:#33cc00;">env->NewStringUTF("callback From Native");</span>

    // 回调该方法,并且传递参数值
    <span style="color:#33cc00;">env->CallVoidMethod(obj, callbackMethodID, native_desc);</span>
}
-------------------------------------------

public native User nativeGetUser();
对应

JNIEXPORT jobject JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_nativeGetUser
        (JNIEnv * env, jobject obj) {
    jclass usercls = <span style="color:#33cc00;">env->FindClass("cn/com/losy/jnitest/jni/User");</span> // 获取User对象引用

    //获得得该类型的构造函数  函数名为 <init> 返回类型必须为 void 即 V
    jmethodID constructMID = <span style="color:#33cc00;">env->GetMethodID(usercls,"<init>","(ILjava/lang/String;)V");</span>

    jstring name = <span style="color:#33cc00;">env->NewStringUTF("HXM from Native");</span>

    // 构造一个对象,调用该类的构造函数,并且传递参数
    jobject userojb = <span style="color:#33cc00;">env->NewObject(usercls, constructMID, 11, name);</span>

    return userojb;
}
--------------------------------------
public native void printUserInfoAtNative(User user);

相关的点包含在下一个方法

--------------------------------------

public native User changeUserInfo(User user);
对应

JNIEXPORT jobject JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_changeUserInfo
        (JNIEnv * env, jobject obj, jobject userobj) {
    jclass usercls = <span style="color:#33cc00;">env->GetObjectClass(userobj);</span> // 或得User类引用

    if(usercls == NULL) {
        std::printf("GetObjectClass failed \n");
    }

    jfieldID ageFieldID = <span style="color:#33cc00;">env->GetFieldID(usercls,"age","I");</span> // 获得得User类的属性id
    jfieldID nameFieldID = <span style="color:#33cc00;">env->GetFieldID(usercls,"name","Ljava/lang/String;");</span> // 获得属性ID

    jint age = <span style="color:#33cc00;">env->GetIntField(userobj, ageFieldID);</span>  // 获得属性值
    jstring name = <span style="color:#33cc00;">(jstring)env->GetObjectField(userobj, nameFieldID);</span>// 获得属性值

    const char * c_name = <span style="color:#33cc00;">env->GetStringUTFChars(name, false);</span>// 转换成 char *

    // 显示user信息
    std::printf("show user info age:%d name:%s", age, c_name);

   <span style="color:#33cc00;"> env->ReleaseStringUTFChars(name, c_name); </span>//释放引用

    // 构造一个jString对象
    const char * c_ptr_name = "quyuan";
    jstring cName = <span style="color:#33cc00;">env->NewStringUTF(c_ptr_name);</span>
    <span style="color:#33cc00;">env->SetObjectField(userobj, nameFieldID, cName);</span> // 设置该字段的值

    <span style="color:#33cc00;">env->SetIntField(userobj, ageFieldID, 55);</span> // 设置该字段的值

    return userobj;
}
-------------------------------------------

public native ArrayList<User> nativeGetUserList(int num);
对应

JNIEXPORT jobject JNICALL Java_cn_com_losy_jnitest_jni_NativeKit_nativeGetUserList
        (JNIEnv * env, jobject obj, jint size) {
    jclass listcls = <span style="color:#33cc00;">env->FindClass("java/util/ArrayList");</span>// 获得ArrayList类引用

    if(listcls == NULL) {
        std::printf("listcls is null \n");
    }
    jmethodID list_construct = <span style="color:#33cc00;">env->GetMethodID(listcls , "<init>","()V");</span>// 获得得构造函数Id
    jobject listobj = <span style="color:#33cc00;">env->NewObject(listcls, list_construct);</span> // 创建一个Arraylist集合对象
    // 获得Arraylist类中的 add()方法ID,其方法原型为: boolean add(Object object) ;
    jmethodID list_add  = <span style="color:#33cc00;">env->GetMethodID(listcls,"add","(Ljava/lang/Object;)Z");</span>
    jclass usercls = <span style="color:#33cc00;">env->FindClass("cn/com/losy/jnitest/jni/User"); </span>// 获得User类引用
    //获得该类型的构造函数  函数名为 <init> 返回类型必须为 void 即 V
    jmethodID user_costruct = <span style="color:#33cc00;">env->GetMethodID(usercls , "<init>", "(ILjava/lang/String;)V");</span>

    for(int i = 0; i < size; i++) {
        jstring str = <span style="color:#33cc00;">env->NewStringUTF("Native");</span>
        //通过调用该对象的构造函数来new 一个 Student实例
        jobject stu_obj = <span style="color:#33cc00;">env->NewObject(usercls , user_costruct, 10, str);</span>  //构造一个对象

        <span style="color:#33cc00;">env->CallBooleanMethod(listobj, list_add, stu_obj);</span> // 执行Arraylist类实例的add方法,添加一个stu对象
    }

    return listobj ;
}
------------------------------------

数据类型 对应关系如下表:(来自百度百科)
Java 类型 本地 C 类型
实际表示的 C 类型
(Win32)
说明
boolean jboolean unsigned char 无符号,8 位
byte jbyte signed char 有符号,8 位
char jchar unsigned short 无符号,16 位
short jshort short 有符号,16 位
int jint long 有符号,32 位
long jlong __int64 有符号,64 位
float jfloat float 32 位
double jdouble double 64 位
void void N/A N/A
Java的String   转换为c的char*








  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值