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 名字空间的函数怎么用了,下一次我们来详细讲他们,并提供一个简单的示例看看效果。

更多相关文章

  1. Android 获取Gmail邮箱地址方法
  2. ubuntu的android studio调试小米手机的方法
  3. android 布局之滑动探究 scrollTo 和 scrollBy 方法使用说明
  4. Android 自定义view 和 onMeasure方法介绍
  5. Android延时执行调用的几种方法
  6. Android Studio 导.so文件简单、高效的方法
  7. android 几个工具方法
  8. Android 判断当前设备是手机还是平板的最有效的方法
  9. Android创建Alert框的方法

随机推荐

  1. xml文件介绍及使用
  2. XML的术语
  3. 第2章 包装类
  4. XML轻松学习总节篇
  5. Java对象的强、软、弱和虚引用
  6. XML轻松学习手册(2)XML概念
  7. JS解析XML文件和XML字符串详解
  8. XML文件结构和基本语法
  9. 了解Xml格式
  10. XML轻松学习手册