小工具知识网

当前位置: 首页 > 行业知识 > Android JNI 复杂数据demo ,字符串、数组对象等数据操作讲解

行业知识

Android JNI 复杂数据demo ,字符串、数组对象等数据操作讲解

2026-11-14 07:47:43 来源:互联网转载

Android JNI 复杂数据demo ,字符串,数组,对象等数据操作

文章目录

  • Android JNI 复杂数据demo ,字符串,数组,对象等数据操作
    • 一、前言
    • 二、Jni复制数据demo
      • 1、MainActivity.java代码
      • 2、native-lib.cpp 代码
      • 3、Demo代码运行后日志打印
      • 三、其他
        • 1、示例中cpp代码调用的主要api函数
        • 2、string 类型的转换和打印
          • jstring打印示例代码:
          • 指针数据合并示例
          • 3、 Android jni cpp代码中接收jintArray并返回jintArray数据示例代码
          • 4、ArrayList\ 数据类型处理
          • 共勉: ���活不是等待暴风雨过去,而是学会在雨中跳舞 。

            一、前言

            Android JNI复杂数据的传输,比如数组,字符串等数据传给底层处理,对于JNI使用不多的开发者,可能开发会比较吃力。

            ()

            本文主要介绍JNI复制数据的传递和打印,不是很复杂的JNI Demo代码,但是对于复杂数据的处理的使用是很有帮助的。

            Android JNI的基础知识介绍,之前已经有介绍,不熟悉的可以先看看:

            ()

            Android Jni的介绍和简单Demo实现:

            https://blog.csdn.net/wenzhi20102321/article/details/136291126

            二、Jni复制数据demo

            1、MainActivity.java代码

            package com.demo.jniobject;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.widget.TextView;import java.util.Arrays;public class MainActivity extends AppCompatActivity {    String TAG = "MainActivity.java";    static {        System.loadLibrary("native-lib");    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Log.i(TAG, "onCreate");        TextView tv = findViewById(R.id.sample_text);        //返回cpp的字符串        String jniString = stringFromJNI();        Log.i(TAG, "onCreate jniString = " + jniString);        tv.setText(jniString);        //传递int值,cpp 返回int值        int intToJNIBack = intToJNI(16);        Log.i(TAG, "onCreate intToJNIBack = " + intToJNIBack);        //传递String值,cpp 返回String值        String stringToJNIBack = stringToJNI("liwenzhi");        Log.i(TAG, "onCreate stringToJNIBack = " + stringToJNIBack);                //传递String和int值,cpp返回String值        String stringAndIntToJNIBack = stringAndIntToJNI("陈wang",18);        Log.i(TAG, "onCreate stringAndIntToJNIBack = " + stringAndIntToJNIBack);                //传递String数值和int数组值,cpp返回String数值的值        String[] listStringAndListIntToJNIBack = listStringAndListIntToJNI(new String[] {"姚pengtao", "朱dejiu","周fuping"},new int[] {20,21,30});        Log.i(TAG, "onCreate listStringAndListIntToJNIBack = " + Arrays.asList(listStringAndListIntToJNIBack));        Log.i(TAG, "onCreate End jniString = " + jniString);    }    //Java到cpp并且获取返回数据方法    public native String stringFromJNI(); //返回cpp的字符串    public native int intToJNI(int age); //传递int值,cpp 返回int值    public native String stringToJNI(String name); //传递String值,cpp 返回String值    public native String stringAndIntToJNI(String name, int age); //传递String和int值,cpp返回String值    public native String[] listStringAndListIntToJNI(String[] names, int[] ages); //传递String数值和int数组值,cpp返回String数值的值}

            上面的方法包含了传递int,String,数组和返回int,String,数值的示例方法调用。

            2、native-lib.cpp 代码

            #include #include #include #define LOG_TAG "native-lib.cpp"#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)//Java 方法: public native String stringFromJNI()extern "C" JNIEXPORT jstring JNICALLJava_com_demo_jniobject_MainActivity_stringFromJNI(        JNIEnv *env,        jobject /* this */) {    std::string hello = "Hello from C++";    LOGI("stringFromJNI hello = %s", hello.c_str());    return env->NewStringUTF(hello.c_str());}//Java 方法: public native int intToJNI(int age)extern "C" JNIEXPORT jint JNICALLJava_com_demo_jniobject_MainActivity_intToJNI(JNIEnv *env, jobject thiz, jint age) {    int cppAge = age +10;    return cppAge;}//Java 方法:public native String stringToJNI(String name)extern "C" JNIEXPORT jstring JNICALLJava_com_demo_jniobject_MainActivity_stringToJNI(JNIEnv *env, jobject thiz, jstring name) {return name;//这里直接返回,想要修改字符串内容可以看看下面代码}//Java 方法:public native String stringAndIntToJNI(String name, int age)extern "C" JNIEXPORT jstring JNICALLJava_com_demo_jniobject_MainActivity_stringAndIntToJNI(JNIEnv *env, jobject thiz, jstring name,jint age) {    // 将jstring转换为C字符串    const char* c_str1 = env->GetStringUTFChars(name, nullptr);    // 进行字符串拼接,不能像Java一样,"" + 5,这样会报错,要先转换类型    // 将jint转换为C++字符串    std::string numStr = std::to_string(age);    std::string result = std::string(c_str1) + "cpp 拼接 age = " + numStr;    LOGD("stringAndIntToJNI name = %s", c_str1);    // 释放GetStringUTFChars函数申请的资源    env->ReleaseStringUTFChars(name, c_str1);    // 将C字符串转换为jstring    return env->NewStringUTF(result.c_str());}//Java 方法:public native String[] listStringAndListIntToJNI(String[] names, int[] ages)extern "C" JNIEXPORT jobjectArray JNICALLJava_com_demo_jniobject_MainActivity_listStringAndListIntToJNI(JNIEnv *env, jobject thiz,jobjectArray names, jintArray ages) {    // 获取数组长度    jint length = env->GetArrayLength(names);    // 创建一个新的数组,用于存储修改后的数据    jobjectArray newArray = env->NewObjectArray(length, env->FindClass("java/lang/String"), nullptr);    // 获取原始int数组的指针    jint *originalArray = env->GetIntArrayElements(ages, nullptr);    // 遍历原始数组    for (int i = 0; i GetObjectArrayElement(names, i);        jint elementInt = originalArray[i] + 2;        std::string numStr = std::to_string(elementInt);        // 将原始字符串转换为新的字符串        const char *c_str = env->GetStringUTFChars(element, nullptr);        std::string modifiedStr = "Modified: ";        modifiedStr += c_str;        modifiedStr += ",age =";        modifiedStr += numStr;        //释放String        env->ReleaseStringUTFChars(element, c_str);        LOGE("listStringAndListIntToJNI modifiedStr = %s", modifiedStr.c_str());        // 创建一个新的字符串对象,并将其设置到新的数组中        jstring newElement = env->NewStringUTF(modifiedStr.c_str());        env->SetObjectArrayElement(newArray, i, newElement);    }    // 释放原始int数组的指针    env->ReleaseIntArrayElements(ages, originalArray, 0);    // 返回修改后的数组    return newArray;}

            里面的代码是不难,但是没写过的,就不知道用哪些api,字符串怎么修改,怎么拼接,打印;

            并且这些知识在网上还是比较片段的,很多是待验证的。

            3、Demo代码运行后日志打印

            2024-03-04 16:24:44.773  I/MainActivity.java: onCreate2024-03-04 16:24:44.773  I/native-lib.cpp: stringFromJNI hello = Hello from C++2024-03-04 16:24:44.773  I/MainActivity.java: onCreate jniString = Hello from C++2024-03-04 16:24:44.773  I/MainActivity.java: onCreate intToJNIBack = 262024-03-04 16:24:44.773  I/MainActivity.java: onCreate stringToJNIBack = liwenzhi2024-03-04 16:24:44.773  D/native-lib.cpp: stringAndIntToJNI name = 陈wang2024-03-04 16:24:44.773  I/MainActivity.java: onCreate stringAndIntToJNIBack = 陈wangcpp 拼接 age = 182024-03-04 16:24:44.773  E/native-lib.cpp: listStringAndListIntToJNI modifiedStr = Modified: 姚pengtao,age =222024-03-04 16:24:44.773 E/native-lib.cpp: listStringAndListIntToJNI modifiedStr = Modified: 朱dejiu,age =232024-03-04 16:24:44.773 E/native-lib.cpp: listStringAndListIntToJNI modifiedStr = Modified: 周fuping,age =322024-03-04 16:24:44.773 I/MainActivity.java: onCreate listStringAndListIntToJNIBack = [Modified: 姚pengtao,age =22, Modified: 朱dejiu,age =23, Modified: 周fuping,age =32]2024-03-04 16:24:44.773 I/MainActivity.java: onCreate End jniString = Hello from C++

            上面日志不仅包含Java代码的日志,同时也包含了cpp文件的日志。

            三、其他

            1、示例中cpp代码调用的主要api函数

            (1)const char* c_str1 = env->GetStringUTFChars(name, nullptr);把jstring name 数据转换成指针对象。(2)jint length = env->GetArrayLength(names);获取 jobjectArray names 数值数据的长度,其他类型的数值数据也是使用这个api就行(3)jobjectArray newArray = env->NewObjectArray(length, env->FindClass("java/lang/String"),nullptr);创建一个length长度的,string数组对象其他类型的数值数据创建就一定不是用NewObjectArray这个函数,比如创建int数组数据类型就用 jintArray newArray = env->NewIntArray(length);其他的基础类型数据把Int换成其他类型就行了。(4)env->SetObjectArrayElement(newArray, i, newElement);给数组 newArray的序号位置 i,设置string数值 std::string modifiedStr其他类型的数值数据创建就一定不是用SetObjectArrayElement这个函数,比如,设置int的数值类型的数据:jint *originalArray; originalArray[1] = 100; //直接就可以替换 (5)jint *originalArray = env->GetIntArrayElements(ages, nullptr);获取 jintArray ages的指针对象。直接使用下标就可以获取到对应的值,比如;int a = originalArray[i]

            这里只是举例说明了某些函数api 的使用,如果会使用这些,那么其他的基础类型那些是没有问题的。

            至于其他一些复杂类型,比如自定义的类操作处理,这些都是不常用的,有需要的自己摸索看看吧。

            2、string 类型的转换和打印

            在jni cpp代码中,基础类型是不用转换就可以直接使用的,但是对象类型的数据要一定要转换的。

            比如下面的简单赋值示例:

            //基础类型,其他类型比如jlong,jchar也是一样的jint number;int num = number + 10;//是没有问题的jstring name;std::string hello = name; //未转换,直接赋值是会报错的const char *c_str = name;//类型是不对应的,会报错
            jstring打印示例代码:
            extern "C" JNIEXPORT jstring JNICALLJava_com_demo_jniobject_MainActivity_stringToJNI(JNIEnv *env, jobject thiz, jstring name) {    // 将jstring转换为C字符串, 打印指针数据    const char *c_str1 = env->GetStringUTFChars(name, nullptr);    LOGD("stringToJNI name = %s", c_str1);    //打印int转换后的string数据    int age = 20;    LOGD("stringToJNI age = %d", age );    std::string numStr = "age = " + std::to_string(age);    LOGD("stringToJNI numStr = %s", numStr.c_str());    return name;}

            cpp中 string类型数据是可以直接使用"+"进行拼接的,拼接后可以直接打印出来;

            int或者其他基本数据类型,如果要和string拼接就要先转换成string类型;

            numStr.c_str()其实也是转换成了指针类型数据,

            所以"%s"打印,打印的是指针数据,知道了指针的第一个位置,字符串后面的数据就都知道了。

            如果要char *类型的数据拼接,就没那么简单了,需要自己对指针位置进行控制,比如:

            指针数据合并示例
            //合并两个指针数据的实现函数char* concatenateStrings(const char* a, const char* b) {    // 获取两个字符串的长度    size_t lenA = strlen(a);    size_t lenB = strlen(b);    // 分配足够的内存来存储合并后的字符串    char* result = new char[lenA + lenB + 1];    // 将第一个字符串复制到合并后的字符串中    strcpy(result, a);    // 将第二个字符串追加到合并后的字符串的末尾    strcat(result, b);    return result;}extern "C" JNIEXPORT jstring JNICALLJava_com_demo_jniobject_MainActivity_stringToJNI(JNIEnv *env, jobject thiz, jstring name) {    // 将jstring转换为C字符串, 打印指针数据    const char *c_str1 = env->GetStringUTFChars(name, nullptr);    LOGD("stringToJNI name = %s", c_str1);        //测试两个指针数据的合并和打印    const char* strA = "Hello, ";    const char* strB = "world!";    // 合并两个字符串    char* mergedString = concatenateStrings(strA, strB);    LOGD("stringToJNI mergedString = %s", mergedString);}

            上面可以看到,指针数据处理需要用到一下api函数。

            3、 Android jni cpp代码中接收jintArray并返回jintArray数据示例代码

            #include extern "C"  JNIEXPORT jintArray JNICALL Java_com_example_example_MyClass_modifyIntArray(JNIEnv *env, jobject obj, jintArray array) {    // 获取数组长度    jint length = env->GetArrayLength(array);    // 创建一个新的int数组,用于存储修改后的数据    jintArray newArray = env->NewIntArray(length);    // 获取原始int数组的指针    jint *originalArray = env->GetIntArrayElements(array, nullptr);    // 遍历原始数组    for (int i = 0; i SetIntArrayRegion(newArray, 0, length, originalArray);    // 释放原始int数组的指针    env->ReleaseIntArrayElements(array, originalArray, 0);    // 返回修改后的int数组    return newArray;}

            上面的创建int类型数值列表的函数和设置数值数据的函数与string类型数组的数据的创建和设置函数是不一样的。

            4、ArrayList 数据类型处理

            比如,一个包含 ArrayList 类型的jni方法:

            //返回的也是 ArrayList 数据类型public native ArrayList modifyArrayList(ArrayList name);

            实际代码中,我就不会这样做了,一般用String[] 就比较好处理,返回再转换成ArrayList会简单很多。

            但是如果一定要实现也也是可以的,就是要写反射代码。

            其实不管是,String还是ArrayList 或者 ArrayList数据,对于cpp代码来说都是object对象。

            对象里面的操作是要通过反射进行的,相对来说比较麻烦,

            比如要先获取对象调用某个api获取里面的某个数据,再调用某个api设置某个数据。

            cpp代码实现:

            #include extern "C" JNIEXPORT jobject JNICALL Java_com_example_example_MyClass_modifyArrayList(JNIEnv *env, jobject obj, jobject arrayList) {    // 获取ArrayList类    jclass arrayListClass = env->GetObjectClass(arrayList);    // 获取ArrayList的size()方法    jmethodID sizeMethod = env->GetMethodID(arrayListClass, "size", "()I");    // 调用size()方法获取ArrayList的大小    jint size = env->CallIntMethod(arrayList, sizeMethod);    // 获取ArrayList的get()方法    jmethodID getMethod = env->GetMethodID(arrayListClass, "get", "(I)Ljava/lang/Object;");    // 获取ArrayList的add()方法    jmethodID addMethod = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");    // 创建一个新的ArrayList用于存储修改后的数据    jobject newArray = env->NewObject(arrayListClass, env->GetMethodID(arrayListClass, "", "()V"));    // 遍历原始ArrayList    for (int i = 0; i CallObjectMethod(arrayList, getMethod, i);        // 将原始字符串转换为C字符串        const char *c_str = env->GetStringUTFChars(element, nullptr);        std::string modifiedStr = "Modified: ";        modifiedStr += c_str;        env->ReleaseStringUTFChars(element, c_str);        // 创建一个新的String对象        jstring newElement = env->NewStringUTF(modifiedStr.c_str());        // 调用add()方法将新的String对象添加到新的ArrayList中        env->CallBooleanMethod(newArray, addMethod, newElement);    }    // 返回修改后的ArrayList    return newArray;}

            上述代码定义了一个名为modifyArrayList的JNI函数,该函数接收一个ArrayList参数,

            并对其中的String元素进行修改(在每个元素前添加"Modified: "),然后返回修改后的ArrayList。

            在代码中,使用JNI函数来获取ArrayList类和其中的方法,通过调用方法来操作ArrayList和其中的元素。

            你可以将上述代码添加到你的CPP文件中,并根据你的需求进行适当修改和调整。

            记得使用JNI提供的函数来获取和操作ArrayList中的元素,并创建新的ArrayList来存储修改后的数据。

            最后,记得返回新的ArrayList对象。

            虽然看完本文不一定能掌握上面的全部知识,但是对于复杂数据处理会有一些概念和认识。

            可以先记录、收藏,到时候不明白的时候再看看,就可以大大提高效率了。

            共勉: 生活不是等待暴风雨过去,而是学会在雨中跳舞 。

上一篇:服务器2012怎么压缩c盘?

下一篇:ea服务器无服务怎么解决?