Android NDK开发之C调用Java及原生代码断点调试(二)
上一篇中,我们主要学习了Java调用本地方法,并列举了两大特殊实例来例证我们的论据,还没学习的伙伴必须先去阅读下,本次的学习是直接在上一篇的基础上进行了。点击:Android NDK开发之从Java与C互调中详解JNI使用(一)
本篇我们主要学习如何从C源码中调用Java代码,以及使用gradle-experimental来调试原生代码。
C 调用 Java 成员变量
- 首先我们现在Java2CJNI类中定义几个成员变量,如下:
这里定义了两个普通成员变量和一个静态成员变量。
就像C不能直接使用Java的引用类型一样,C也不能直接的访问Java成员变量,而是通过JNI所封装的API来调用Java成员。通常会有如下的步骤:
1:获取java实例对象的引用
2:通过实例对象获取java成员变量ID
3:通过变量ID获取java成员变量
那么我们现在分步的讲下学习以上的步骤。
获取java实例对象的引用
获取实例对象的引用JNI已为我们封装好了方法,我们可以使用GetObjectClass函数来获取class对象:
jclass (GetObjectClass)(JNIEnv, jobject);
例如:
jclass class = (*env)->GetObjectClass(env, jobj);
jobj对象我们在上一节也讲过,这个是Java调用本地方法时,JNI会封装调用类的一个实例,在这里就是Java2CJNI类的引用。
另外一种方法也是可以获取到class对象,就是通过反射机制来获取对象:
jclass (FindClass)(JNIEnv, const char*);
例如:
jclass class = (*env)->FindClass(env, "com/sanhui/ndkdemo/Java2CJNI");
同上面的方法一样,都可以获取Java对象引用。
通过实例对象获取java成员变量ID
由第一步我们获取到了实例对象的引用,那么我们可以通过JNI封装的方法来获取实例内的变量ID:
①:获取普通成员变量ID
通过jfieldID (GetFieldID)(JNIEnv, jclass, const char, const char);可以获取到一个jfieldID 类型的ID。
GetFieldID函数中第三个参数是Java类中的成员变量的名称,如果在Java2CJNI类中定义的成员private String codeError = "验证码错误 !"中codeError 。第四个参数是变量签名,说白点就是Java类中成员变量的返回类型,如变量codeError 的返回类型是String ,但是String在原生代码中属于引用类型,不能直接识别,所以在JNI中有相应的签名映射,如下表:
Java 类型 | JNI 签名映射 |
---|---|
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | J |
float | F |
double | D |
fully-qualified-class | Lfully-qualified-class ; |
type[] | [type |
method type | ( arg-types ) ret-type |
上述中基本数据类型的签名多以大写类型首字母为主,但是引用类型是使用“L”+ 类型路径 + “;”,如String类型则是“Ljava/lang/String;”,数组则是"[" + 类型,如“[I”表示整形数组,如果是Java方法则是“(参数的类型) + 返回值类型” 。
ok,通过GetFieldID获取成员变量ID,如:
jfieldID codeErrorID = (*env)->GetFieldID(env,jclazz,"codeError","Ljava/lang/String;");
②:获取静态成员变量ID
成员变量分为普通和静态变量,那获取静态变量该如何呢?JNI也为我们封装好了方法:
jfieldID (GetStaticFieldID)(JNIEnv, jclass, const char,
const char);
通过GetStaticFieldID函数可以获取到静态变量ID,参数如①一样。举例:
jfieldID loginSuccID = (*env)->GetStaticFieldID(env,jclazz,"loginSucc","Ljava/lang/String;");
通过变量ID获取java成员变量
ok,获取完变量ID,我们就可以通过ID来取得变量了,这里获取成员变量也是分为静态和普通,分别使用:
jobject (GetObjectField)(JNIEnv, jobject, jfieldID);和jobject (GetStaticObjectField)(JNIEnv, jclass, jfieldID);函数。
例如:
jstring jcodeError = (*env)->GetObjectField(env,jobj,codeErrorID);
jstring juserNameError = (*env)->GetObjectField(env,jobj,userNameErrorID);
jstring jloginSucc = (*env)->GetStaticObjectField(env,jclazz,loginSuccID);
ok,到这里我们就获取到了Java类中的成员变量,来看下整体代码:
获取到Java中的成员变量,然后返回到Java中,然后我们通过Toast给打印出来:
来看下执行结果,是否如我们多料想的一样:
从上图中我们看到的执行结果显然和我们在Java2CJNI中定义的loginSucc成员变量是一样的,由此可以得出结论就是C成功的调用了Java类中的变量。
注意:由于获取Java类中的变量需要在原生代码中调用几个方法才能获取到最终的结果,对于性能来说要求的开销过大,所以建议一般不这样直接轰C中调用Java的成员变量,如果有需要建议以参数的形式传递给原生代码。
C 调用 Java 方法
C调用Java方法和调用成员变量基本是一样的,首先我们现在Java类中定义一个方法,用Toast来显示信息,如:
上面也说过C调用Java方法和变量步骤基本一样,下面来看下基本步骤:
1:获取java实例对象的引用
2:通过实例对象获取实例方法ID
3:通过方法ID调用实际的Java方法
获取java实例对象的引用
这一步和C获取变量所介绍的获取方式是一样的,都是通过GetObjectClass或是FindClass函数来获取的,这里就不再赘述,可以参考上面的实力。
通过实例对象获取实例方法ID
java中方法分为两类,一类是普通的方法,一类是静态方法。下面来逐一的介绍。
①:获取普通方法ID:
可以通过jmethodID (GetMethodID)(JNIEnv, jclass, const char, const char);来获取方法ID,这也是JNI已经封装好的原生方法,来解释下这个函数:
GetMethodID函数前两个参数就不必多介绍了,其中第三个参数是Java类中的方法名称,对应的是Java2CJNI类中定义的方法:public void showMessage(String message){}中的showMessage。第四个参数是方法签名,也就是Java类中方法的返回类型,至于什么是签名上面已介绍清楚。
获取方法ID实例:
jmethodID showMessage = (*env)->GetMethodID(env,jclazz,"showMessage","(Ljava/lang/String;)V");
这里和变量唯一不同的是,方法有可能带参数,那么签名就需要带上参数签名和返回值签名,也就是在()里的是参数签名,()外的是返回值签名,如“(Ljava/lang/String;)V”表示是含有一个String类型的参数和一个void的无返回类型。
②:获取静态方法ID:
获取静态方法ID会使用JNI的 jmethodID (GetStaticMethodID)(JNIEnv, jclass, const char, const char);函数,它的使用和参数与GetMethodID一样,并没有什么差别。
例如:
jmethodID showMessage = (*env)->GetStaticMethodID(env,jclazz,"showMessage","(Ljava/lang/String;)V")
3:通过方法ID调用实际的Java方法
获取到方法ID后,我们可以通过JNI提供的回调函数来真正的调用Java方法,这里也是分为回调普通方法和静态方法,由于两者基本没什么差别,我们这里就只讲下普通方法的回到。
C回调Java方法会使用Call< type >Method函数来回调实际的方法,例如,我们调用我们显示Toast的无返回值方法:
(*env)->CallVoidMethod(env,jobj,showMessage,jloginSucc);
直接的调用CallVoidMethod,它第三个参数传入的是jmethodID类型的方法ID,由之前获取到的,第四个参数是要传递给Java的参数,这里接受的是一个String累的字符串。
ok,通过上面的三个步骤,我们已调用了Java的方法了,来看下整体的C代码实现吧。
好,来看下执行结果:
ok,到这里C调用Java就讲完了,下面讲下实用的C原生代码怎么断点调试。
Androidstudio中原生代码断点调试
下载C/C++调试器LLDB
在androidstudio->File->settings->androidSDK->SDK Tools中下载LLDB:
在项目目录下的build.gradle文件添加对gradle-experimental的依赖
用项目对gradle-experimental的依赖代替原本项目对gradle的依赖。
配置工程下的build.gradle
① 使用com.android.model.application替代原来的com.android.application
② 把原有的配置存放在model{}中
③ 所有的配置属性使用等号(=)连接
DUG运行
原生C代码中断点,然后执行dug运行模式。
ok,接下来就可以执行你的源代码了。
好了,今天就讲到这里吧。
请关注微信公众号。谢谢
Android NDK开发之C调用Java及原生代码断点调试(二)的更多相关文章
- Android NDK开发之Jni调用Java对象
https://my.oschina.net/zhiweiofli/blog/114064 通过使用合适的JNI函数,你可以创建Java对象,get.set 静态(static)和 实例(instan ...
- Android NDK开发篇(五):Java与原生代码通信(数据操作)
尽管说使用NDK能够提高Android程序的运行效率,可是调用起来还是略微有点麻烦.NDK能够直接使用Java的原生数据类型,而引用类型,由于Java的引用类型的实如今NDK被屏蔽了,所以在NDK使用 ...
- Android NDK开发篇(四):Java与原生代码通信(原生方法声明与定义与数据类型)
Java与原生代码通信涉及到原生方法声明与定义.数据类型.引用数据类型操作.NIO操作.訪问域.异常处理.原生线程 1.原生方法声明与定义 关于原生方法的声明与定义在上一篇已经讲一点了,这次具体分析一 ...
- android webView开发之js调用java代码示例
1.webView设置 webView.getSettings().setJavaScriptEnabled(true);//设置支持js webView.addJavascriptInterface ...
- Android NDK开发篇(六):Java与原生代码通信(异常处理)
一.捕获异常 异常处理是Java中的功能.在Android中使用SDK进行开发的时候常常要用到.Android原生代码在运行过程中假设遇到错误,须要检測,并抛出异常给Java层.运行原生代码出现了问题 ...
- Android NDK开发之从Java与C互调中详解JNI使用(一)
生活 这一个礼拜过得真的是苦不堪言,上周因为打球脚踝直接扭伤,肿的想猪蹄一样,然后休息几天消肿了,可以缓慢龟速的行走了,然而五一回来上班第一天,上班鞋子还能穿上,下班脚已插不进鞋子里面了,好吧,又肿回 ...
- Android NDK开发之Android.mk文件
Android NDK开发指南---Android.mk文件 博客分类: Android NDK开发指南 Android.mk文件语法详述 介绍: ------------ 这篇文档是用来描述你的 ...
- Android NDK开发之Jni的数据类型
在前面的一篇博客<Android NDK开发简介>,我简单地说明了Android NDK开发的流程,以及其重要的一环:JNI层得开发.今天我再详细说明一下自己的学习经验. JNI是Java ...
- Android混合开发之WebViewJavascriptBridge实现JS与java安全交互
前言: 为了加快开发效率,目前公司一些功能使用H5开发,这里难免会用到Js与Java函数互相调用的问题,这个Android是提供了原生支持的,不过存在安全隐患,今天我们来学习一种安全方式来满足Js与j ...
随机推荐
- java学习笔记 --- java基础语法
一.java标识符,关键字,保留字 1.标识符 用来增强程序阅读性自定义的名字.类名,变量名,方法名等都可以被称为标识符 标识符的组成: 1.由数字(0-9),字母(a-z,A-Z),下划线(_),美 ...
- Chrome 33+ 自建 扩展 实现 custom.css
http://bbs.kafan.cn/thread-1674386-1-2.html
- WEB开发性能优化--核心定义介绍篇(1)
推荐理由 随着 互联网的蓬勃发展,并且伴随着产品功能的越来越复杂,对于技术人员来说最大的挑战就是如何在保证业务快速发展的同时,也可保证不断复杂的业务对用户体验的影响,其中对用户来说最重要的体验指标是如 ...
- line-height属性总结
line-height属性的继承性: 子元素不设置line-height时, 在父元上设置带单位的值和百分比时会先计算父元素的line-height大小然后继承过来,在父元素上设置无单位的数值时,子 ...
- 微服务架构的简单实现-Stardust
微服务架构,一个当下比较火的概念了.以前也只是了解过这方面的概念,没有尝试过.想找找.NET生态下面是否有现成的实现,可是没找到,就花了大半个月的闲暇时间,遵循着易用和简单,实现了一个微服务框架,我叫 ...
- 老李分享:SSL协议相关证书
老李分享:SSL协议相关证书 poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:9088214 ...
- 老李推荐:第5章1节《MonkeyRunner源码剖析》Monkey原理分析-启动运行: 官方简介
老李推荐:第5章1节<MonkeyRunner源码剖析>Monkey原理分析-启动运行: 官方简介 在MonkeyRunner的框架中,Monkey是作为一个服务来接受来自Monkey ...
- POPTEST培训:web自动化测试之DOM
POPTEST培训:web自动化测试之DOM poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq ...
- Oracle的基本学习(二)—基本查询
一.基本查询语句 (1)查看当前用户 show user; (2)查看当前用户下的表 select * from tab; (3)查看员工表的结构 desc emp; (4)选择全部列 S ...
- JavaWeb总结(九)—过滤器
一.Filter简介 Web开发人员通过Filter技术,对Web服务器管理的所有Web资源:JSP.Servlet.静态文件.静态HTML等进行拦截,从而实现一些特殊的功能.例如实现URL级别的权限 ...