行业知识
Android JNI 复杂数据demo ,字符串、数组对象等数据操作讲解
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服务器无服务怎么解决?