Qt 5.3之后,新增了 QtAndroid 名字空间,内有下列四个方法:

  • QAndroidJniObject AndroidActivity()
  • int androidSdkVersion()
  • void startActivity(const QAndroidJniObject & intent, int receiverRequestCode, QAndroidActivityResultReceiver * resultReceiver = 0)
  • void startIntentSender(const QAndroidJniObject & intentSender, int receiverRequestCode, QAndroidActivityResultReceiver * resultReceiver = 0)

我的书《Qt on Android核心编程》写作时用的 Qt SDK 版本为 5.2.0(第一个正式支持Android的Qt SDK版本),写作过程中 5.3 发布,书里只是简单介绍了下 QtAndroid 的存在,对上面的几个方法没有做实际研究,从本文开始,我就以 Qt 5.3.1 为基础,来展开介绍 QtAndroid 名字空间以及与其密切相关的 QAndroidJniObject 。

QAndroidJniObject

在介绍 QtAndroid 里面的方法之前,必须要介绍 QAndroidJniObject 这个类。因为要使用 Qt 提供的 JNI 功能编程,离开 QAndroidJniObject 可谓寸步难行。

QAndroidJniObject 属于 androidextras 模块,要使用它,需要在 pro 文件中加入下面的代码:

QT += androidextras

androidextras 是从 Qt 5.2 引入的。这个模块内还包括了 QAndroidJniEnvironment 类,QAndroidJniEnvironment 代表 JNI 环境,也就是通常我们使用 JNI 编程时的 JNIEnv 。我们使用 Qt 进行 JNI 编程时,构造一个 QAndroidJniEnvironment 对象,即可获得 JNIEnv 指针,可以进一步使用 JNIEnv 的方法来实现特定功能,比如检查 JNI 调用过程中是否发生了异常、清理异常等等。更多的细节请参考 Qt 帮助和 jni.h(JDK中有此头文件,可打开浏览) 。

我们的重头戏是 QAndroidJniObject 。

QAndroidJniObject 是对原始 JNI 类型的封装,代表了一个 Java 对象(类的实例),它提供了很多方法供开发者使用,我把它分为三类:

  • 构造一个 Java 对象
  • 调用 Java 静态(类)方法
  • 调用 Java 实例方法

咱们一个一个来看。

构造 Java 对象

要调用 Java 类库,就需要构造 Java 对象,这是第一步,可能也是最难的一步。不过相信随着本文的介绍,你很快就会清楚如何构造一个对应于 Java 对象的 JNI 对象。

QAndroidJniObject 提供了下列构造函数来创建 JNI 对象:

  1. QAndroidJniObject()
  2. QAndroidJniObject(const char * className)
  3. QAndroidJniObject(const char * className, const char * signature, ...)
  4. QAndroidJniObject(jclass clazz)
  5. QAndroidJniObject(jclass clazz, const char * signature, ...)
  6. QAndroidJniObject(jobject object)

还有一个静态的方便方法供我们从一个 QString 对象来构造 Java 中的 String 对象:

  • QAndroidJniObject fromString(const QString & string)

fromString 不必讲了,简单明了。咱们来看构造函数吧。

构造函数又分了两类,一类是根据 Java 类名和 Java 类构函数签名来创建 JNI 对象;一类是根据已有的 JNI 对象(也可结合 Java 构造函数签名)来创建 QAndroidJniObject 对象。我们再简化之,只看 1 、 2 、 3 ,从 Java 类名来创建 QAndroidJNIObject ,那,第一个构造函数无参数,也不介绍了,剩下就俩了。

使用JNI的两点基础

Java 的全路径类名,是带了包名的。 Java 中的包,可以类比于 C++ 里的 namespace ,一个包里可以包含了多个类,一方面方便将关联的类组织到一起,另一方面也可以避免名字冲突。

我们以 String 类来说明。

String 类在包 java.lang 中,全路径类名为 java.lang.String 。当我们在 C++ 用以字符串方式描述一个 Java 类时,需要把 “.” 替换为 “/” ,如 java.lang.String ,字符串描述为 “java/lang/String” 。又如 android.content.Intent ,字符串描述为 “android/content/Intent” 。

好啦,这是我们在 Qt 中使用 QAndroidJniObject 进行 JNI 编程的第一个基础。

既然有第一个,就有第二个喽。木错,第二个基础就是方法签名。

关于方法签名,我在《Qt on Android核心编程》一书的 15.2.1 节有详尽介绍,Qt 帮助中检索 QAndroidJniObject 也有介绍,这里我们只简单说明一下。

方法签名的表述形式:(Arguments)ReturnType 。圆括号中是参数列表,紧跟圆括号的是返回值类型。例如“(I)C”的意思就是参数为 int ,返回值为 char 。当一个 Java 方法的参数或返回值类型为对象时,需要使用全路径类名,并且加前缀“L”和后缀“;”。如“(Ljava/lang/String;)Ljava/lang/String;”,表示一个方法的参数类型为 String ,返回值也是 String 。

方法签名就介绍到这里了。详细的参考我的书或者 Qt 帮助。

QAndroidJniObject(const char * className)

参数为 “const char * className” 的构造函数,只根据类名来构造 JNI 对象,调用 className 指定的 Java 类的默认构造函数。

我们明白了如何用字符串描述一个 Java 类,QAndroidJniObject(const char*)这个构造函数就很容易理解了。

举几个例子来看。

Intent 类是 Android 提供的、用于组件间通信的一种机制。通过 Intent ,你可以调用其它的系统功能或第三方提供的功能,比如你可以调用拨打电话的功能,可以显示联系人,也可以调用相机。我们在使用 Intent 时可以指定一个 action ,action 代表你要做的动作,也就是你想干啥;还可以在 Intent 中携带数据给被调用的一方。使用起来非常方便。Intent 的全路径类名为 android.content.Intent 。

我们可以这么构造一个 Intent 对象:

QAndroidJniObject intent("android/content/Intent");

上面的代码就会调用 Intent 的默认构造函数来构造一个 Intent 对象。

String 类的全路径类名为 java.lang.String ,可以这么构造一个空的 String 对象:

QAndroidJniObject str("java/lang/String");

值得注意的是,使用 QAndroidJniObject(const char * className) 这个构造函数来创建 Java 对象,一定要确保你指定的 Java 类有无参构造函数(即默认构造函数)。

QAndroidJniObject(const char * className, const char * signature, ...)

这个参数根据类名 className 和 指定签名的构造函数来创建 JNI 对象。

还是以 Intent 为例, Intent 有一个构造函数,原型是:Intent(String action) 。我们就根据这个构造函数来创建 Intent 对象,C++ 代码如下:

    QAndroidJniObject action = QAndroidJniObject::fromString("android.settings.SETTINGS");QAndroidJniObject intent("android/content/Intent","(Ljava/lang/String;)V", action.object<jstring>());

在上面的代码中,我们先使用 QAndroidJniObject::fromString 构造了一个 Java 的 String 对象作为我们的 action ,这个 action 会打开 Android 系统设置。

然后我们调用 Intent 的 Intent(String action)构造函数来创建一个 Intent 类。 构造函数没有返回值,参数为 String, 所以函数签名是“(Ljava/lang/String;)V”。QAndroidJniObject 的 object 方法返回它所持有的 java 对象, action.object<jstring>() 返回的就是 jstring 喽。

后面我们会看到如何使用 Intent 类来调用 Android 系统中的其它组件。

调用 Java 实例方法

有了 Java 对象,我们就可以调用这个对象的实例方法。

所谓实例方法,就是一个类的非静态方法,需要先有对象才能调用。而类方法,指的就是类的静态方法,后面会讲到怎么调用类方法,这里先讲如何调用实例方法。

QAndroidJniObject 提供了下列方法以方便我们调用 Java 实例方法:

  1. T callMethod(const char * methodName) const
  2. T callMethod(const char * methodName, const char * signature, ...) const
  3. QAndroidJniObject callObjectMethod(const char * methodName) const
  4. QAndroidJniObject callObjectMethod(const char * methodName, const char * signature, ...) const

这四个方法,前两个是一组,后两个是一组。

两个 callMethod 方法,调用 Java 对象的那些返回值为基础类型(如int、double等)的实例方法。两个 callObjectMethod 方法则调用 Java 对象的那些返回值为对象(类实例)的实例方法。我觉得区别在这里,不知道对不对啊。

两个 callMethod ,一个带参数,一个不带参数;callMethod 是模板方法,模板参数为 Java 方法的返回值的类型。两个 callObjectMethod 类似。

我们来看个例子,计算字符串的长度。C++代码如下:

QAndroidJniObject action = QAndroidJniObject::fromString("android.settings.SETTINGS");
int = action.callMethod<jint>("length");

上面的代码调用 fromString 创建一个 Java String 对象,然后使用 callMethod 调用 String 的 “int length()” 方法来获取字符串的长度。

再来看一个使用 callObjectMethod 的例子,C++代码如下:

QAndroidJniObject action = QAndroidJniObject::fromString("android.settings.SETTINGS");
jint start = 4;
QAndroidJniObject substring = action.callObjectMethod("substring", "(I)Ljava/lang/String", start);

上面的代码,调用 Java String 类的 “String substring(int start)”方法来获取子串。

调用Java类(静态)方法

调用 Java 类方法,无需构造 Java 对象,因为类方法是静态方法,是属于类的,不需要对象就可以调用。

QAndroidJniObject 提供了下列静态方法来调用 Java 类方法:

  • T callStaticMethod(const char * className, const char * methodName)
  • T callStaticMethod(const char * className, const char * methodName, const char * signature, ...)
  • T callStaticMethod(jclass clazz, const char * methodName)
  • T callStaticMethod(jclass clazz, const char * methodName, const char * signature, ...)
  • QAndroidJniObject callStaticObjectMethod(const char * className, const char * methodName)
  • QAndroidJniObject callStaticObjectMethod(const char * className, const char * methodName, const char * signature, ...)
  • QAndroidJniObject callStaticObjectMethod(jclass clazz, const char * methodName)
  • QAndroidJniObject callStaticObjectMethod(jclass clazz, const char * methodName, const char * signature, ...)

callStaticMethod 有四个,直接调用 Java 类的静态方法,需要一个模版参数,对应于 Java 类方法的返回值。

callStaticObjectMethod 也有四个,可以调用 Java 类的那些返回对象的静态方法。

看一些简单的代码片段:

jint a = 2;
jint b = 4;
jint max = QAndroidJniObject::callStaticMethod<jint>("java/lang/Math", "max", "(II)I", a, b);...
QAndroidJniObject thread = QAndroidJniObject::callStaticObjectMethod("java/lang/Thread", "currentThread","()Ljava/lang/Thread;");
...
QAndroidJniObject string = QAndroidJniObject::callStaticObjectMethod("java/lang/String", "valueOf", "(I)Ljava/lang/String;", 10);

上面的代码片段实际上是三个小示例。

第一个小示例,调用 java.lang.Math 来求两个数中较大的那个。“ int max(int, int) ”用来完成“求两数中较大那个”这一功能。

第二个小示例,调用 java.lang.Thread 的 currentThread方法获取当前的线程对象,currentThread 方法没有参数,返回值是 Thread 对象。

第三个小示例,调用 java.lang.String 的 valueOf 方法把一个数字转换为字符串。valueOf 原型为 “ String valueOf(int)”。

----------

好啦,这次我们就介绍到这里,有了本文的基础,就可以接着看 QtAndroid 名字空间的函数怎么用了,下一次我们来详细讲他们,并提供一个简单的示例看看效果。

QtAndroid详解(1):QAndroidJniObject相关推荐

  1. QtAndroid详解(2):startActivity和它的小伙伴们

    上一篇,"QtAndroid详解(1):QAndroidJniObject",我们做了好多好多准备工作,目的就是为使用 QtAndroid 名字空间里的 startActivity ...

  2. 从命令行到IDE,版本管理工具Git详解(远程仓库创建+命令行讲解+IDEA集成使用)

    首先,Git已经并不只是GitHub,而是所有基于Git的平台,只要在你的电脑上面下载了Git,你就可以通过Git去管理"基于Git的平台"上的代码,常用的平台有GitHub.Gi ...

  3. JVM年轻代,老年代,永久代详解​​​​​​​

    秉承不重复造轮子的原则,查看印象笔记分享连接↓↓↓↓ 传送门:JVM年轻代,老年代,永久代详解 速读摘要 最近被问到了这个问题,解释的不是很清晰,有一些概念略微模糊,在此进行整理和记录,分享给大家.在 ...

  4. docker常用命令详解

    docker常用命令详解 本文只记录docker命令在大部分情境下的使用,如果想了解每一个选项的细节,请参考官方文档,这里只作为自己以后的备忘记录下来. 根据自己的理解,总的来说分为以下几种: Doc ...

  5. 通俗易懂word2vec详解词嵌入-深度学习

    https://blog.csdn.net/just_so_so_fnc/article/details/103304995 skip-gram 原理没看完 https://blog.csdn.net ...

  6. 深度学习优化函数详解(5)-- Nesterov accelerated gradient (NAG) 优化算法

    深度学习优化函数详解系列目录 深度学习优化函数详解(0)– 线性回归问题 深度学习优化函数详解(1)– Gradient Descent 梯度下降法 深度学习优化函数详解(2)– SGD 随机梯度下降 ...

  7. CUDA之nvidia-smi命令详解---gpu

    nvidia-smi是用来查看GPU使用情况的.我常用这个命令判断哪几块GPU空闲,但是最近的GPU使用状态让我很困惑,于是把nvidia-smi命令显示的GPU使用表中各个内容的具体含义解释一下. ...

  8. Bert代码详解(一)重点详细

    这是bert的pytorch版本(与tensorflow一样的,这个更简单些,这个看懂了,tf也能看懂),地址:https://github.com/huggingface/pytorch-pretr ...

  9. CRF(条件随机场)与Viterbi(维特比)算法原理详解

    摘自:https://mp.weixin.qq.com/s/GXbFxlExDtjtQe-OPwfokA https://www.cnblogs.com/zhibei/p/9391014.html C ...

最新文章

  1. 子段乘积(逆元费马小定理)+线段树做法
  2. ubuntu12.04安装lamp的简单lamp
  3. TOMCAT报错:HTTP Status 404 -
  4. Your shell has not been properly configured to use 'conda activate'
  5. ajaxfileupload 访问不到后台_一套简单通用的Java后台管理系统,拿来即用,非常方便(附项目地址)...
  6. 粒子群优化算法(2)离散粒子群算法
  7. echarts--(2)--创建一个饼图
  8. repo报错:SyntaxError: invalid syntax
  9. 1.3 Go语言上手-高质量编程与性能调优实战
  10. pycharm interpreter invalid
  11. 软件测试学习心得-5
  12. 笔记本处理器排名_【笔记本】AMD YES,霸占性能排行榜前四
  13. db2iupgrade失败:DBI1205E One or more local databases cannot be upgraded
  14. WIN7安装官方漏洞补丁,错误代码0x80240037的解决方法
  15. Moho Pro - Mac 上一款专业的二维动画制作软件,强大的功能让你尽情发挥创意
  16. 新仙剑奇侠传java,新仙剑奇侠传问题
  17. XiaoHu日志 9/5~9/6
  18. 搜索-Query Understanding (QU)
  19. 极光笔记 | 用 WhatsApp 进行海外用户运营的 N 个理由
  20. linux下有没有蜘蛛纸牌游戏,在Linux下可用Wine安装和运行蜘蛛纸牌、浩方电竞平台...

热门文章

  1. 【Tkinter】终于把StringVar讲明白了
  2. 当前速度火车测试 软件,四大主流火车购票APP对比测评教程
  3. Python packing tools not found.
  4. OCI : ORA-24333: zero iteration count
  5. php类的定义与实例化方法
  6. android中 cdf文件的作用是什么意思,行情艰难,Android初中高级面试题,附详细答案...
  7. 从0到1搭建属于自己的服务器
  8. 财路网每日原创推送:区块链技术热下的“冷思考”
  9. 2022-2027年中国NGB网络建设光通信器件行业市场深度分析及投资战略规划报告
  10. C程序--输出大写英文字母