Skip to content

Files

Latest commit

822f1cf · Jan 1, 2022

History

History
188 lines (133 loc) · 10.5 KB

File metadata and controls

188 lines (133 loc) · 10.5 KB

五十九、安卓意图概述

到本书的这个阶段,应该很清楚安卓应用由一个或多个活动组成。然而,还有一个领域尚未得到详细阐述,那就是一项活动触发另一项活动的启动的机制。正如标题为“安卓应用的剖析”一章中简要概述的,这主要是通过使用意图来实现的。

在完成以下章节中基于 AndroidStudio 的意图示例实现之前,本章的目标是以显式意图和隐式意图的形式概述意图,并介绍意图过滤器。

59.1 意图概述

意图(android.content.Intent )是一个活动能够启动另一个活动的消息传递系统。例如,一个活动可以发出一个意图,请求启动包含在同一应用中的另一个活动。然而,意图也超越了这个概念,允许一个活动请求设备上为其配置权限的任何其他适当注册的活动的服务。例如,考虑一个应用中包含的活动,该活动需要加载网页并向用户显示。应用不必包含第二个活动来执行这个任务,代码可以简单地向安卓运行时发送一个意图,请求已经注册了显示网页能力的任何活动的服务。运行时系统会将请求与设备上的可用活动相匹配,并启动匹配的活动,或者在多个匹配的情况下,允许用户决定使用哪个活动。

意图还允许将数据从发送活动传输到接收活动。例如,在前面概述的场景中,发送活动需要将要显示的网页的 URL 发送给第二个活动。类似地,接收活动也可以被配置为当所需任务完成时将数据返回给发送活动。

尽管在后面的章节中没有涉及,但也值得强调的是,除了启动活动之外,意图还用于启动服务和广播接收器并与之通信。

意图分为显性的和隐性的。

59.2 显式意图

显式意图通过引用目标活动的组件名(实际上是类名)来请求启动特定的活动。当启动与发送活动驻留在同一个应用中的活动时,这种方法最常见(因为应用开发人员知道类名)。

显式意图是通过创建意图类的实例,传递要启动的活动的活动上下文和组件名称来发出的。然后调用 startActivity()方法,将意图对象作为参数传递。例如,下面的代码片段为将要启动的类名为 ActivityB 的活动发出了一个意图:

val i = Intent(this, ActivityB::class.java)
startActivity(i)

在通过调用意图对象的 putExtra()方法启动之前,可以通过将数据添加到意图对象来将数据传输到接收活动。数据必须以键值对的形式添加。下面的代码扩展了前面的示例,将分别带有“myString”和“myInt”键的字符串和整数值添加到意图中:

val i = Intent(this, ActivityB::class.java)
i.putExtra("myString", "This is a message for ActivityB")
i.putExtra("myInt", 100)

startActivity(i)

目标活动接收的数据是 Bundle 对象的一部分,可以通过调用 getIntent()获得。getExtras()。Activity 类的 getIntent()方法返回启动活动的意图,而 getExtras()方法(Intent 类的)返回包含数据的 Bundle 对象。例如,要提取传递给活动 b 的数据值:

val extras = intent.extras ?: return

val myString = extras.getString("myString")
int myInt = extras.getInt("MyInt")

当使用意图在同一个应用中启动其他活动时,这些活动必须列在应用清单文件中。为包含名为 ActivityA 和 ActivityB 的活动的应用正确配置了以下 AndroidManifest.xml 内容:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.ebookfrenzy.intent1.intent1" >

    <application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:label="@string/app_name"
            android:name="com.ebookfrenzy.intent1.intent1.ActivityA" >
            <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="ActivityB"
            android:label="ActivityB" >
        </activity>
    </application>
</manifest>

59.3 从活动中返回数据

正如上一节中的示例所示,当数据传输到 ActivityB 时,数据无法返回到第一个活动(我们称之为 ActivityA)。然而,这可以通过将活动 b 作为活动 a 的子活动来实现。通过调用 startActivityForResult()方法而不是使用 startActivity()来启动意图,从而将活动作为子活动启动。除了意图对象之外,这个方法还被传递了一个请求代码值,当子活动返回时,该值可以用来标识返回数据。例如:

startActivityForResult(i, REQUEST_CODE)

为了将数据返回给父活动,子活动必须实现 finish()方法,其目的是创建一个包含要返回的数据的新意图对象,然后调用封闭活动的 setResult()方法,传递一个结果代码和包含返回数据的意图。结果代码通常是 RESULT_OK 或 RESULT_CANCELED,但也可能是一个自定义值,取决于开发人员的要求。在子活动崩溃的情况下,父活动将收到一个 RESULT_CANCELED 结果代码。

例如,下面的代码说明了典型子活动 finish()方法的代码:

override fun finish() {
    val data = Intent()

    data.putExtra("returnString1", "Message to parent activity")

    setResult(RESULT_OK, data)
    super.finish()
}

为了获取和提取返回的数据,父活动必须实现 onActivityResult()方法,例如:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) {
    if ((requestCode == request_code) &&
            (resultCode == RESULT_OK)) {

        if (data.hasExtra("returnString1")) {
            val returnString = data.extras.getString("returnString1")
        }
    }
}

请注意,上面的方法检查返回的请求代码值,以确保它与传递给 startActivityForResult()方法的值相匹配。当启动多个子活动时,使用请求代码来跟踪哪个活动当前正在返回结果尤其重要,因为所有活动在退出时都会调用同一个 onActivityResult()方法。

59.4 隐式意图 s

与引用要启动的活动的类名的显式意图不同,隐式意图通过指定要执行的操作和接收活动要处理的数据类型来标识要启动的活动。例如,带有 URI 对象形式的网页的网址的 ACTION_VIEW 的动作类型将指示安卓系统搜索并随后启动具有网络浏览器功能的活动。当在安卓设备上执行时,以下隐式意图将导致指定的网页出现在网络浏览器活动中:

val intent = Intent(Intent.ACTION_VIEW,
        Uri.parse("http://www.ebookfrenzy.com"))

startActivity(intent)

当上述隐式意图由活动发出时,安卓系统将使用称为意图解析的过程,在设备上搜索已经注册了处理 http 方案数据上的 ACTION_VIEW 请求的能力的活动。如果找到单个匹配项,将启动该活动。如果找到多个匹配项,将提示用户从可用的活动选项中进行选择。

59.5 使用意图过滤器

意图过滤器是一种机制,通过这种机制,活动向安卓意图解析过程“通告”支持的动作和数据处理能力。继续上一节中的示例,一个能够显示网页的活动将在其清单文件中包含一个意图过滤器部分,指示对 http 方案数据上的 ACTION_VIEW 类型的意图请求的支持。

需要注意的是,发送和接收活动都必须为要执行的操作类型请求许可。这是通过在两个活动的清单文件中添加 >标签来实现的。例如,以下清单行请求访问 internet 和联系人数据库的权限:

<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.INTERNET"/>

下面的 AndroidManifest.xml 文件说明了一个名为 WebViewActivity 的活动的配置,该活动为互联网访问启用了意图过滤器和权限:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.ebookfreny.WebView"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="10" />

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:label="@string/app_name"
            android:name=".MainActivity" >
            <intent-filter>
               <action android:name="android.intent.action.VIEW" />
               <category android:name="android.intent.category.DEFAULT" />
               <data android:scheme="http" />
            </intent-filter>
        </activity>
    </application>

</manifest>

59.6 检查意向可用性

假设某个活动可用于某个特定意图通常是不明智的,尤其是因为缺少匹配的操作通常会导致应用崩溃。幸运的是,在将活动发送到运行时系统之前,可以识别特定意图的活动可用性。以下方法可用于确定特定意图操作类型的活动的可用性:

fun isIntentAvailable(context: Context, action: String): Boolean {
    val packageManager = context.packageManager
    val intent = Intent(action)
    val list = packageManager.queryIntentActivities(intent,
            PackageManager.MATCH_DEFAULT_ONLY)
    return list.size > 0
}

59.7 总结

意图是一种消息传递机制,通过它一个安卓活动可以启动另一个。显式意图通过按类名引用接收活动来引用要启动的特定活动。当启动包含在同一个应用中的活动时,通常使用显式意图,尽管不是排他的。隐式意图指定要执行的操作和要处理的数据类型,并让安卓运行时找到匹配的活动来启动。隐式意图通常在启动驻留在不同应用中的活动时使用。

活动可以通过将数据以键值对的形式捆绑到意图对象中,从而将数据发送到接收活动。数据只能从作为发送活动的子活动启动的活动中返回。

活动通过应用清单文件中的意图过滤器规范向安卓意图解析过程通告功能。发送和接收活动还必须请求适当的权限来执行任务,例如访问设备联系人数据库或互联网。

在介绍了意图理论之后,接下来的几章将在 AndroidStudio 中创建一些例子,将显式和隐式的意图付诸行动。