上一篇,“QtAndroid详解(1):QAndroidJniObject”,我们做了好多好多准备工作,目的就是为使用 QtAndroid 名字空间里的 startActivity() 方法调用 Android 系统功能奠定基础。那这次呢,我们就要来研究如何使用 startActivity 方法了。
在我的书《Qt on Android核心编程》中,讲解 JNI ,介绍如何使用 JNI 扩展 Qt 应用时,是通过重写 QtActivity ,为我们的 Activity 加入静态方法来实现的,实际上从 Qt 5.3.0 以后,有更方便的方法,那就是接下来要介绍的 startActivity 了。
startActivity ,方法名字已经道出了它的功能:启动一个活动(Activity)。简单的说,在 Android 里,Activity就是布满整个窗口或者悬浮于其他窗口上的交互界面。通常用户可见的功能,都是与 Activity 关联的,比如我们用微信、微博、美团,我们看到的那些界面,可操作的东东,都是与 Activity 息息相关的。
行,背景就这么多吧,要是感兴趣,可以问下度娘,或者到 https://developer.android.com/index.html 上看看 Android 的在线文档。不过貌似打不开哈……
我们先详细介绍一下 startActivity 方法,然后再举例来看使用 startActivity 调用一个活动的两种情况。
startActivity方法详解
startActivity 方法原型如下:
- void startActivity(const QAndroidJniObject & intent, int receiverRequestCode, QAndroidActivityResultReceiver * resultReceiver = 0);
如你所见,startActivity 有三个参数。
第一个是 intent ,我们在“QtAndroid详解(1):QAndroidJniObject”中举例时已经介绍了 Android 提供的 Intent 类,这里的 intent 参数实际上就是一个 Java Intent 对象。
第二个参数是 receiverRequestCode ,实际上是一个标识符,用来标记一次 startActivity 调用,当你启动一个 Activity 时,这个 request code 会传递过去,当你调用的 Activity 结束时,你会得到一个通知,这个通知里又把你传的 request code 带回给你,同时呢,这个通知还带回来你调用的那个 Activity 执行的结果,叫作 result code 。更厉害的是,你调用的 Activity ,还可以传递更多的数据回来,而这些数据呢,又是通过一个 Intent 携带的。
为了处理你调起的 Activity 的返回结果,在 Qt 代码中,我们需要一个 QAndroidActivityResultReceiver 对象,这就是第三个参数了。
实际上, Qt 提供的 startActivity 方法是个混搭,根据参数的不同,分别对应了 Android Activity 类的 startActivity(Intent) 和 startActivityForResult(Intent, int) 两个方法。追本溯源,还是来看看 Android 的文档吧,这样更清楚些。
Android 中的 startActivity 和 startActivityForResult
首先看 startActivity ,它是 Activity 类的方法,原型如下:
- void startActivity(Intent intent);
在 Android 文档中有两处对 startActivity 的描述:
“
1. Launch a new activity. You will not receive any information about when the activity exits.
2. The startActivity(Intent) method is used to start a new activity, which will be placed at the top of the activity stack. It takes a single argument, an Intent, which describes the activity to be executed.
”
我翻译一下,中文如下:
“
1. 启动一个新活动。当新活动结束时,你收不到任何消息。
2. startActivity(Intent) 方法用来启动一个新活动,这个新活动将被放在活动栈的顶端。这个方法接受一个意图(Intent)作为参数,而作为参数的意图,描述了要执行的活动的相关信息。
”
哦嘛,就是这啦,startActivity 启动一个与调用者分离的活动,新活动起来后就自生自灭,调用者既不关心它的死活也不关心它干嘛有什么后果。
再来看 startActivityForResult 方法,它也是 Activity 的方法。原型如下:
- public void startActivityForResult (Intent intent, int requestCode);
从名字就可以看出来,startActivityForResult ,启动一个新活动的目的就是为了拿到新活动的结果。就是说,它对新活动很 care ,你要干个事儿,还得带个结果回来给我。比方说你从通讯录里挑个人儿给我,那你结束时,如果谁也没挑,那就告诉我失败;如果你挑了个人儿,就告诉我成功,并且还得让我有地方获得你挑的那个人儿的信息。
intent 参数与 startActivity 的参数含义完全一样,不必再提。
requestCode 参数,用来标识一次调用,当新 Activity 结束后,这个 requestCode 会通过 onActivityResult() 方法返回给调用方。这里的 requestCode ,与 QtAndroid 提供的 startActivity 的第二个参数是一致的。
那接下来该说到怎么接收新 Activity 的返回结果了。
Android中 Activity 类的 onActivityResult 方法
当我们使用 startActivityForResult 启动一个新的 Activity 时,目的是为了做点什么事儿获取一些信息,那如何知道新 Activity 结束了、执行情况如何、带回什么东东呢? Android 为 Activity 类设计了 onActivityResult 方法,我们在 extends Activity 时可以重写这个方法,根据 requestCode 来区分一次调用,判断调用结果,抽取新 Activity 携带的数据。
onActivityResult 方法原型如下:
- protected void onActivityResult (int requestCode, int resultCode, Intent data);
如你所见,它有三个参数。
第一个参数 requestCode 是我们调用 startActivityForResult 时传递的。
第二个参数 resultCode 是新 Activity 的执行结果,有两种,一种是 RESULT_OK(实际上为 -1 ),一种是 RESULT_FAILED(实际上是0)。 这两个值有点诡异啊,不符合我的习惯。当你在 C++ 中来编程时,不使用 RESULT_OK 和 RESULT_FAILED 两个常量,直接使用数字字面量,就容易搞反(C的习惯,0是OK,-1是FAILED)。
第三个参数是一个 Intent 对象,携带了新 Activity 返回的数据。
Android中的Intent
前面不止一次提到 Android 的 Intent ,对 Android 开发人员来讲 Intent 是很熟悉的一种存在,对 Qt 开发人员来讲 Intent 可能还是有些陌生,我们再花点篇幅介绍一下。
Intent 在 android.content 包中,全路径类名为 android.content.Intent ,我们在“QtAndroid详解(1):QAndroidJniObject”中举例时也提到过。
Intent 类是 Android 提供的、用于组件间通信的一种机制。它是待执行操作的一个抽象描述,它可以与 startActivity() 配合来启动一个活动,与 broadcastIntent() 配合来发送一个广播,也可以与 startService() 或 bindService() 配合以便与后台服务进行通信。
Intent 最常见的用途就是启动活动,通过 Intent ,你可以调用其它的系统功能或第三方提供的功能,比如你可以调用拨打电话的功能,可以显示联系人,也可以调用相机。
我们在使用 Intent 时可以指定一个 action ,action 代表你要做的动作,也就是你想干啥;还可以在 Intent 中携带数据给被调用的一方。而被调起的一方(Activity或Service)则可以通过 Intent 的 getData() 、 getBundle() 、 getXxxExtra() 等方法来获取调用者传递过来的数据。Intent 就像 Android 组件之间的信使,可以告诉我们要做什么,以及有哪些数据可用。
Intent 有好几个构造函数,我们可能会用到下面两个:
- Intent(String action)
- Intent(String action, Uri uri)
Intent 还提供了很多方法,允许我们设置 action 和 data ,下面列出的仅仅是一小部分:
- setAction(String action)
- putExtra(String name, int value)
- putExtra(String name, CharSequence value)
- putExtra(String name, Bundle value)
- setData(Uri data)
要想用 Intent 调用某个组件,就需要指定 action ,那 action 到底是什么玩意儿呢?
action 实际上是一个字符串,代表了某个组件,当你传递一个 Intent 对象给 startActivity() 等方法时, Android 框架会来解析这个 Intent 的 action ,找到 action 代表的组件并调用它。解析的过程比较复杂,也有很多规则,我们可以简单的理解为 Android 有一张表,登记了系统中的各种组件,当你给出"com.android.settings.SETTINGS"这样的action时,Android就会在这个表中查找到设置组件并启动它。Intent预定义了很多 action ,感兴趣的可以到 Android 在线帮助中查看 Intent 的文档。
好啦,Android 中与 startActivity 相关的背景知识就介绍到这里吧,是时候回过头来看我们的 QtAndroid 了。
QAndroidActivityResultReceiver
Qt 使用 startActivity 一个方法对应了 Android 的 startActivity 和 startActivityForResult 两个方法。当我们提供 requestCode 和 resultReceiver 两个参数时,就对应 Android Activity 的方法 startActivityForResult ,而 resultReceiver 参数就是与 Android Activity 的 onActivityResult 方法适配的,它的类型是 QAndroidActivityResultReceiver 。
QAndroidActivityResultReceiver 是一个纯虚类,定义了一个接口,原型如下:
- virtual void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject & data) = 0;
看到了吧,handleActivityResult 方法和 Android Activity 的 onActivityResult 是完全匹配的。唯一不同的是,第三个参数转换为了 QAndroidJniObject ,实际上就是一个 Intent 对象。
为了处理 Activity 的返回结果,我们需要实现 QAndroidActivityResultReceiver 接口,比如:
- class ResultReceiver: public QAndroidActivityResultReceiver
- {
- public:
- int requestId;
- void handleActivityResult(int receiverRequestCode, int resultCode, const QAndroidJniObject & data)
- {
- if(receiverRequestCode == requestId)
- {
- if(resultCode == RESULT_OK)
- {
- //some code here
- }
- else
- {
- //some code here
- }
- }
- }
- };
需要注意的是,QAndroidActivityResultReceiver 是被异步调用的,因此当你提供一个对象用于接收新调起的 Activity 的返回结果时,这个对象最好是从堆上构造(new),如果你把 QAndroidActivityResultReceiver 放在栈上,很可能带来灾难性的后果,当 Qt 收到被调起的 Activity 的返回结果时,你提供的 receiver 可能已经析构了,你就悲剧了,崩溃吧你。
调用Android活动
我乖,终于来了吗……
OK,要说到如何调用 Android Activity 了,真不容易啊,铺垫忒长。
在 Qt 中调用 Android Activity ,根据是否关注结果来划分,有两种方式:
- 告知式调用,不管结果
- 追问式调用,要求结果
不管结果的调用可能是酱紫的:
- QAndroidJniObject action = QAndroidJniObject::fromString("android.settings.SETTINGS");
- QAndroidJniObject intent("android/content/Intent","(Ljava/lang/String;)V", action.object<jstring>());
- startActivity(intent, 0);
关注结果的调用可能是酱紫的:
- ResultReceiver *receiver = new ResultReceiver(1);
- QAndroidJniObject action;
- QAndroidJniObject intent("android/content/Intent","(Ljava/lang/String;)V", action.object<jstring>());
- startActivity(intent, 1, receiver);
不知不觉又写了这么长了,看来实例要等到下一次了……