Android Studio NDK 新手教程(5)--Java对象的传递与改动
概述
本文主要Java与C++之间的对象传递与取值。包括传递Java对象、返回Java对象、改动Java对象、以及性能对照。
通过JNIEnv完毕数据转换
Java对象是存在于JVM虚拟机中的,而C++是脱离JVM而执行的,假设在C++中訪问和使用Java中的对象。必定会使用JNIEnv这个桥梁。事实上通过以下的代码非常easy看出,这样的訪问方式和Java中的反射十分雷同。
这里定义一个简单Java对象用于下文測试:
package com.example.wastrel.hellojni;
/**
* Created by wastrel on 2016/8/24.
*/
public class Bean {
private String msg;
private int what;
public Bean(String msg,int what)
{
this.msg=msg;
this.what=what;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getWhat() {
return what;
}
public void setWhat(int what) {
this.what = what;
}
@Override
public String toString() {
return "Msg:"+msg+";What:"+what;
}
}
从C++中创建一个Java对象并返回
//Java中的native方法声明
public native Bean newBean(String msg,int what);
//C++中的方法实现
JNIEXPORT jobject JNICALL Java_com_example_wastrel_hellojni_HelloJNI_newBean
(JNIEnv *env, jobject obj, jstring msg,jint what){
//先找到class
jclass bean_clz=env->FindClass("com/example/wastrel/hellojni/Bean");
//在实际应用中应该确保你的class、method、field存在。
降低此类推断。
if(bean_clz==NULL)
{
LOGE("can't find class");
return NULL;
}
//获取构造函数。构造函数的返回值是void,因此这里方法签名最后为V
jmethodID bean_init=env->GetMethodID(bean_clz,"<init>","(Ljava/lang/String;I)V");
if(bean_init==NULL)
{
LOGE("can't find init function");
return NULL;
}
//然后调用构造函数获得bean
jobject bean=env->NewObject(bean_clz,bean_init,msg,what);
return bean;
}
注:假设提示找不到NULL 请
include<stddef.h>
C++中解析Java对象
//java方法Native声明
public native String getString(Bean bean);
//C++中的方法实现
JNIEXPORT jstring JNICALL Java_com_example_wastrel_hellojni_HelloJNI_getString
(JNIEnv *env, jobject obj,jobject bean){
jclass bean_clz=env->FindClass("com/example/wastrel/hellojni/Bean");
//这部分是通过get函数去获取相应的值
// jmethodID bean_getmsg=env->GetMethodID(bean_clz,"getMsg","()Ljava/lang/String;");
// jmethodID bean_getwhat=env->GetMethodID(bean_clz,"getWhat","()I");
// jstring jmsg=(jstring)env->CallObjectMethod(bean,bean_getmsg);
// jint what=env->CallIntMethod(bean,bean_getwhat);
//这部分是通过类的成员变量直接取获取值。你可能注意到在Java中定义的变量都是private修饰的。但在反射的调用下是毫无作用的。
jfieldID bean_fmsg=env->GetFieldID(bean_clz,"msg","Ljava/lang/String;");
jfieldID bean_fwhat=env->GetFieldID(bean_clz,"what","I");
jstring jmsg=(jstring)env->GetObjectField(bean,bean_fmsg);
jint what=env->GetIntField(bean,bean_fwhat);
//将拿到的值拼装一个String返回回去
const char * msg=env->GetStringUTFChars(jmsg,NULL);
char *str=new char[1024];
sprintf(str,"Msg:%s;What:%d(From C++)",msg,what);
jstring rs=env->NewStringUTF(str);
delete []str;
env->ReleaseStringUTFChars(jmsg,msg);
return rs;
}
注:sprintf函数包括在stdio.h头文件里
C++中改动Java对象属性值
//java方法Native声明
public native void ModifyBean(Bean bean);
//C++实现
JNIEXPORT void JNICALL Java_com_example_wastrel_hellojni_HelloJNI_ModifyBean
(JNIEnv *env, jobject obj,jobject bean){
jclass bean_clz=env->FindClass("com/example/wastrel/hellojni/Bean");
jfieldID bean_fmsg=env->GetFieldID(bean_clz,"msg","Ljava/lang/String;");
jfieldID bean_fwhat=env->GetFieldID(bean_clz,"what","I");
jstring msg=env->NewStringUTF("Modify in C++");
//又一次设置属性
env->SetObjectField(bean,bean_fmsg,msg);
env->SetIntField(bean,bean_fwhat,20);
return;
}
结果图
//java中调用代码
HelloJNI helloJNI=new HelloJNI();
Bean bean=helloJNI.newBean("This is from C++ bean",10);
tv.setText(bean.toString());
bean=new Bean("This is from Java bean",15);
tv.append("\n"+helloJNI.getString(bean));
helloJNI.ModifyBean(bean);
tv.append("\n"+bean.toString());
Java中new Object和C++中new Object的性能对照
以下我们通过一个測试函数来比較通过两种方式的性能,这里能够毫无疑问的告诉你,Java一定比C++的快。那么这个对照的意义就在于,使用C++创建Java对象的时候会不会造成不可接受的卡顿。
这里使用的測试机是华为Mate7,详细硬件配置可自行百度。
測试函数例如以下:
void Test(int count)
{
long startTime=System.currentTimeMillis();
for (int i=0;i<count;i++)
{
new Bean("123",i);
}
long endTime=System.currentTimeMillis();
Log.e("Java","Java new "+count+"s waste "+(endTime-startTime)+"ms");
HelloJNI helloJNI=new HelloJNI();
startTime=System.currentTimeMillis();
for (int i=0;i<count;i++)
{
helloJNI.newBean("123",i);
}
endTime=System.currentTimeMillis();
Log.e("C++","C++ new "+count+"s waste "+(endTime-startTime)+"ms");
}
測试结果:
Java: Java new 5000s waste 3ms
C++: C++ new 5000s waste 38ms
Java: Java new 10000s waste 6ms
C++: C++ new 10000s waste 79ms
Java: Java new 50000s waste 56ms
C++: C++ new 50000s waste 338ms
Java: Java new 100000s waste 60ms
C++: C++ new 100000s waste 687ms
通过结果能够看出,通过C++来new对象比Java慢了足足10倍左右。可是从时间上来讲。假设仅仅是在C++中new一个Java对象。几个微秒的时间差距全然是能够忽略不计的。
或许有人就会说。C++慢那么多是由于每次都在FindClass,GetMethodId。而在程序执行过程中这两个值是不会改变的。听起来确实有这样一个原因。以下我们将C++中的代码稍作改动缓存jclass和jmethodId。
改动后的newBean函数:
//用静态变量缓存
static jclass bean_clz=NULL;
static jmethodID bean_init=NULL;
JNIEXPORT jobject JNICALL Java_com_example_wastrel_hellojni_HelloJNI_newBean
(JNIEnv *env, jobject obj, jstring str,jint what){
//先找到class
if(bean_clz==NULL)
{
jclass _bean_clz=env->FindClass("com/example/wastrel/hellojni/Bean");
bean_clz=(jclass)env->NewGlobalRef(_bean_clz);
}
//获取构造函数。构造函数的返回值是void,因此这里方法签名最后为V
if(bean_init==NULL)
{
bean_init=env->GetMethodID(bean_clz,"<init>","(Ljava/lang/String;I)V");
}
//然后调用构造函数获得bean
jobject bean=env->NewObject(bean_clz,bean_init,str,what);
return bean;
}
你可能发现了缓存方法ID和缓存jclass似乎不一样。那是由于jclass事实上是java.lang.Class对象,而方法ID是JNI中定义的一个结构体。假设这里不使用
env—>NewGlobalRef()
函数声明其是一个全局引用的话,在执行的时候可能就会报错:JNI ERROR (app bug): accessed stale local reference 0x5900021
;表明在Jvm中该对象已经被回收了。引用已经失效了。而NewGlobalRef的作用就在于告诉JVM。C++中一直持有该引用,请不要回收。显然这又引发了另外一个问题。你须要在你不须要该引用的时候告诉JVM,那么就须要调用env->DelGlobalRef()
。当然你也能够不调用。那么该Java对象将在你的程序关闭的时候被回收。
測试结果:
Java: Java new 5000s waste 3ms
C++: C++ new 5000s waste 18ms
Java: Java new 10000s waste 5ms
C++: C++ new 10000s waste 24ms
Java: Java new 50000s waste 44ms
C++: C++ new 50000s waste 121ms
Java: Java new 100000s waste 65ms
C++: C++ new 100000s waste 259ms
这次的结果表明,假设缓存方法ID和jclass能缩短一半的时间。但仍然不如Java快。这也非常好理解。C++创建Java对象终于还是通过Java创建的,重复的通过反射去创建自然不如自身创建来得快。
总结
- JNI中想訪问Java Object方法签名、类名和变量名十分重要,一旦确定了就不要轻易单方面改动Java中的定义。由于这会导致JNI找不到相关的方法或类等,而引发JNI错误。
- 尽管JNI提供了各种方法来完毕Java的反射操作,可是请酌情使用,由于这会让Java代码与C++代码之间过度依赖。
- 当你须要返回C++中的结构体数据的时候,能够考虑把结构体转换成相应的Java对象返回。
Android Studio NDK 新手教程(5)--Java对象的传递与改动的更多相关文章
- Android Studio NDK 学习之接受Java传入的字符串
本博客是基于Android Studio 1.3 preview版本,且默认你已经安装了Android SDK, Android NDK. 用Android Studio新建一个工程叫Prompt,其 ...
- Android Studio NDK 学习之接受Java传入的Int数组
本博客是基于Android Studio 1.3 preview版本,且默认你已经安装了Android SDK, Android NDK. 用Android Studio新建一个工程叫AndroidJ ...
- Android Studio NDK开发-JNI调用Java方法
相对于NDK来说SDK里面有更多API可以调用,有时候我们在做NDK开发的时候,需要在JNI直接Java中的方法和变量,比如callback,系统信息等.... 如何在JNI中调用Java方法呢?就需 ...
- Android Studio NDK编程初探
继上一篇学习了如何使用NDK编译FFMPEG后,接下来就是要学习如何在Android Studio中使用了. 经过参考和一系列的摸索,记录下具体步骤. 创建C++ Support的Android St ...
- 第七章 : Git 介绍 (上)[Learn Android Studio 汉化教程]
Learn Android Studio 汉化教程 [翻译]Git介绍 Git版本控制系统(VCS)快速成为Android应用程序开发以及常规的软件编程领域内的事实标准.有别于需要中心服务器支持的早期 ...
- [Learn Android Studio 汉化教程]第三章:使用 Android Studio 编程
[Learn Android Studio 汉化教程]第三章:使用 Android Studio 编程 本章包含如何在 Android Studio 中书写或生成代码. Android Studio ...
- 第六章:Reminders实验:第二部分[Learn Android Studio 汉化教程]
Learn Android Studio 汉化教程 Reminders Lab: Part 2 This chapter covers capturing user input through the ...
- 第五章:Reminders实验:第一部分[Learn Android Studio 汉化教程]
Learn Android Studio 汉化教程 By now you are familiar with the basics of creating a new project, program ...
- 第三章:使用 Android Studio 编程[Learn Android Studio 汉化教程]
Learn Android Studio 汉化教程 Android Studio 本章包含如何在Android Studio中书写或生成代码. Android Studio 使用面向对象编程的思想来生 ...
随机推荐
- (转)linux samb服务器配置
1. 首先安装samba服务. 2. 配置防火墙(要先关闭) 3. 创建samba服务器用户:smbpasswd -a username 删除samba服务器用户:smbpasswd -x u ...
- POJ2245 Lotto
Lotto Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 6634 Accepted: 4201 Description ...
- spin_lock浅析【转】
转自:http://blog.csdn.net/frankyzhangc/article/details/6569475 版权声明:本文为博主原创文章,未经博主允许不得转载. 今天我们详细了解一下sp ...
- Oracle基础 09 概要文件 profile
--创建 profile 概要文件create profile profile123 limit failed_login_attempts 2; --修改用户的 profile 文件alter u ...
- 学习apache commons lang3的源代码 (1):前言和R
本系列主要是针对lang3的3.7版本的源代码进行学习,并适当举例.一共大概150多个java文件,争取30天内学习完毕. 26个英文字母 争取每天学习1个字母开头的类们. 今天,就学习R开头的吧. ...
- 关系和非关系型数据库区别(以及oracle和mysql的区别)
一.关系型数据库 关系型数据库,是指采用了关系模型来组织数据的数据库. 关系模型是在1970年由IBM的研究员E.F.Codd博士首先提出的,在之后的几十年中,关系模型的概念得到了充分的发展并逐 ...
- Codeforces 626 B. Cards (8VC Venture Cup 2016-Elimination Round)
B. Cards time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...
- Java的Hashtable类(转)
文章来源:http://blog.csdn.net/zhna123_2011/article/details/6741479 ps:直接copy 哈希表是一种重要的存储方式,也是一种常见的检索方法.其 ...
- HBASE启动失败,Failed construction of Master: class org.apache.hadoop.hbase.master.HMaster
Master日志错误:2015-12-02 06:34:32,394 ERROR [main] master.HMasterCommandLine: Master exitingjava.lang.R ...
- HDU 1180 诡异的楼梯【BFS/楼梯随时间变化】
诡异的楼梯 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others) Total Submis ...