android jni jobjectArray存储输出不同类型的数据
1.jni的定义
JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少要保证本地代码能工作在任何Java 虚拟机环境。
2.android中为什么使用jni
我的理解是:
1.代码保护性提高,c++编译成的动态库不易被反编译。
2.补充java的性能,很多优秀的开源库使用c/c++,在性能上c/c++更快。
3.android使用jni弊端
1.程序的稳定性变弱,native日志不易跟踪和解决,需要扎实的c++编写,java c++ 之间数据结构转换需要小心,程序整体接口封装稍显冗余。
4.android使用jni,java 与c++数据结构转换
1.基本数据类型
2.引用数据类型
3.jobjectArray如何存储任何类型数据
这里重点讲一下 jobjectArray如何存储各种数据类型。jni有自己的一套数据转换规则,如果是基本数据类型,或者某一类的数据类,很好处理,每种都有对应的方式,但是如果是多种数据结构,存储到jni里面,就会稍显麻烦,这点在单纯的java c++却不存在,直接用顶级 object类就可以,但是jni里面却是编译不过。代码中类或者字段赋值用 XXX YYY VVV MMM 等表示。
JNIEXPORT jobjectArray JNICALL Java_com_xxx_xxx_xxx_jni_1GetAllObject
(JNIEnv *env, jclass , jlong instance )
{
XXX*xxx= (XXX*) instance;
MyArray<VVV> arrVar;
YYY yyy;
((XXX*)xxx)->GetXXXs(yyy);
jint nSize = arrVar.GetSize();
// 获取类对象
jclass cls = env->FindClass("java/lang/Object");
jobjectArray mjobjectArray = (jobjectArray)env->NewObjectArray(nSize, cls, NULL);
jint nIndex = 0;
for (jint i = 0; i < nSize; i++)
{
switch(arrVar[i].GetType())
{
case VVV::Boolean:
{
jboolean myjboolean=MMM;
const char *mychar;
if(myjboolean){
mychar="true";
}else{
mychar="false";
}
jstring mystring=env->NewStringUTF(mychar);
env->SetObjectArrayElement(mjobjectArray,
nIndex,(jobject)mystring);
nIndex++;
}
break;
case VVV::Short:
{
jshort myjshort=MMM;
char buf[64];
sprintf(buf,"%d",myjshort);
jstring mystring=env->NewStringUTF(buf);
env->SetObjectArrayElement(mjobjectArray,
nIndex,(jobject)mystring);
nIndex++;
}
break;
case VVV::Integer:
{
jint myjint=MMM;
char buf[64];
sprintf(buf,"%d",myjint);
jstring mystring=env->NewStringUTF(buf);
env->SetObjectArrayElement(mjobjectArray,
nIndex,(jobject)mystring);
nIndex++;
}
break;
case VVV::Long:
{
jlong myjlong=MMM;
char buf[64];
sprintf(buf,"%d",myjlong);
jstring mystring=env->NewStringUTF(buf);
env->SetObjectArrayElement(mjobjectArray,
nIndex,(jobject)mystring);
nIndex++;
}
break;
case VVV::Float:
{
jfloat myfloat=MMM;
char buf[128];
sprintf(buf,"%f",myfloat);
jstring mystring=env->NewStringUTF(buf);
env->SetObjectArrayElement(mjobjectArray,
nIndex,(jobject)mystring);
nIndex++;
}
break;
case VVV::Double:
{
jdouble myjdouble=MMM;
char buf[128];
sprintf(buf,"%f",myjdouble);
jstring mystring=env->NewStringUTF(buf);
env->SetObjectArrayElement(mjobjectArray,
nIndex,(jobject)mystring);
nIndex++;
}
break;
case VVV::Date:
case VVV::Time:
case VVV::TimeStamp:
{
jniString str =MMM;
env->SetObjectArrayElement(mjobjectArray,
nIndex, (jobject)str);
nIndex++;
}
break;
case VVV::String:
{
jniString str = arrVar[i].ToString();
env->SetObjectArrayElement(mjobjectArray,
nIndex, (jobject)str);
nIndex++;
}
break;
default:
// 以上类型都不满足,返回nullptr;
const char *mychar="NULL";
jstring mystring=env->NewStringUTF(mychar);
env->SetObjectArrayElement(mjobjectArray,
nIndex, (jobject)mystring);
nIndex++;
//delete mychar;
break;
}
}
}
return mjobjectArray;
}
以上就是 各种数据结构转换为objectArray输出的例子,核心思想是在填充数据元素的时候需要注意:env>SetObjectArrayElement(mjobjectArray, nIndex, (jobject)mystring); 所以必须将第三个变量转换为jstring 然后向上转型为 jobject类型,有人会说 草,既然是jobject,我为毛只能转为jstring才能向上转型呢? 这点在其实和 java c++处理上真好不一样。请看下图
图中很明白 jstring基类 是 jobject ,而基本数据类型 jint jboolean jlong等基类不是jobect,所以编译会出错,需要都格式化成字符串一起存储在 jobjectArray里面。
3.jobjectArray 总结
有人会问,为什么非得存进一个里面,单独输出不可以吗? 每种都有对应的array。 当然这样是可以的,但是上层的话需要开很多组件接口,另外效率上面差距非常大。 项目中使用 查询实体各种数据类型的一些属性, 之前单独的存储 和jobjectArray的存储差距是非常大的,大概size 200左右的时候,提升了150-200倍的时间效率。