本文档介绍了应用与其他应用进行交互的一些常见用例。每个部分都提供了指南,指导您如何在软件包可见性有限的情况下实现应用的功能;如果您的应用以 Android 11(API 级别 30)或更高版本为目标平台,则需要考虑这一点。
如果应用以 Android 11 或更高版本为目标平台,并要使用 intent 在其他应用中启动 activity,最直接的方法就是调用 intent 并在没有应用可用时处理 ActivityNotFoundException
异常。
如果应用的部分功能需要知道对 startActivity()
的调用是否可以成功(例如显示界面),请向应用清单的 <queries>
元素添加一个元素。通常,这是一个 <intent>
元素。
打开网址
本部分将介绍在以 Android 11 或更高版本为目标平台的应用中打开网址的各种方法。
在浏览器或其他应用中打开网址
如需打开网址,请使用包含 ACTION_VIEW
intent 操作的 intent,如本指南的加载网址部分所述。使用此 intent 调用 startActivity()
后,会发生以下某种情况:
- 网址在网络浏览器应用中打开。
- 网址在支持该网址作为深层链接的应用中打开。
- 系统显示一个消除歧义对话框,让用户选择使用哪个应用打开网址。
因为设备上未安装可打开网址的应用而发生
ActivityNotFoundException
。(这种情况不常见。)建议让应用在发生
ActivityNotFoundException
时捕获并处理此异常。
由于 startActivity()
方法不需要软件包可见性来启动另一个应用的 activity,因此您无需向应用清单中添加 <queries>
元素,也无需对现有的 <queries>
元素进行任何更改。这适用于打开网址所用的隐式 intent 和显式 intent。
检查浏览器是否可用
在某些情况下,应用在尝试打开网址之前,可能需要验证设备上是否至少有一个可用浏览器,或者某个特定浏览器是否为默认浏览器。在此类情况下,请在清单的 <queries>
元素中添加以下 <intent>
元素:
<!-- Place inside the <queries> element. --> <intent> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="https" /> </intent>
当您调用 queryIntentActivities()
并将网络 intent 作为参数传递时,在某些情况下,返回的列表就会包含可用的浏览器应用。如果用户将网址配置为默认在非浏览器应用中打开,此列表将不包含浏览器应用。
在自定义标签页中打开网址
使用自定义标签页,应用就可以自定义浏览器的外观和风格。您可以在自定义标签页中打开网址,而无需在应用清单中添加或更改 <queries>
元素。
不过,您可能需要检查设备是否有支持自定义标签页的浏览器,或者使用 CustomTabsClient.getPackageName()
选择要通过自定义标签页启动的特定浏览器。在此类情况下,请在清单中将以下 <intent>
元素添加为 <queries>
元素的一部分:
<!-- Place inside the <queries> element. --> <intent> <action android:name="android.support.customtabs.action.CustomTabsService" /> </intent>
让非浏览器应用处理网址
即使您的应用可以使用自定义标签页打开网址,我们仍建议您尽可能让一款非浏览器应用打开网址。如需在您的应用中提供此功能,请尝试使用一个用于设置 FLAG_ACTIVITY_REQUIRE_NON_BROWSER
intent 标志的 intent 来调用 startActivity()
。如果系统抛出 ActivityNotFoundException
,您的应用就可以在自定义标签页中打开网址。
如果 intent 包含此标志,在发生以下任一种情况时,调用 startActivity()
都会导致抛出 ActivityNotFoundException
:
- 此调用会直接启动一款浏览器应用。
- 此调用会向用户显示一个消除歧义对话框,其中只提供了浏览器应用作为选项。
以下代码段展示了如何更新逻辑来使用 FLAG_ACTIVITY_REQUIRE_NON_BROWSER
intent 标志:
Kotlin
try { val intent = Intent(ACTION_VIEW, Uri.parse(url)).apply { // The URL should either launch directly in a non-browser app (if it's // the default) or in the disambiguation dialog. addCategory(CATEGORY_BROWSABLE) flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_REQUIRE_NON_BROWSER } startActivity(intent) } catch (e: ActivityNotFoundException) { // Only browser apps are available, or a browser is the default. // So you can open the URL directly in your app, for example in a // Custom Tab. openInCustomTabs(url) }
Java
try { Intent intent = new Intent(ACTION_VIEW, Uri.parse(url)); // The URL should either launch directly in a non-browser app (if it's the // default) or in the disambiguation dialog. intent.addCategory(CATEGORY_BROWSABLE); intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_REQUIRE_NON_BROWSER); startActivity(intent); } catch (ActivityNotFoundException e) { // Only browser apps are available, or a browser is the default. // So you can open the URL directly in your app, for example in a // Custom Tab. openInCustomTabs(url); }
避免出现消除歧义对话框
如果您不想在用户打开网址时显示消除歧义对话框,而是更想在此类情况下自行处理网址,您可以使用一个用于设置 FLAG_ACTIVITY_REQUIRE_DEFAULT
intent 标志的 intent。
如果 intent 包含此标志,调用 startActivity()
会导致在该调用本应向用户显示消除歧义对话框时系统抛出 ActivityNotFoundException
。
如果 intent 同时包含此标志和 FLAG_ACTIVITY_REQUIRE_NON_BROWSER
intent 标志,在发生以下任一种情况时,调用 startActivity()
都会导致抛出 ActivityNotFoundException
:
- 此调用会直接启动浏览器应用。
- 此调用会向用户显示消除歧义对话框。
以下代码段展示了如何将 FLAG_ACTIVITY_REQUIRE_NON_BROWSER
和 FLAG_ACTIVITY_REQUIRE_DEFAULT
标志配合使用:
Kotlin
val url = URL_TO_LOAD try { // For this intent to be invoked, the system must directly launch a // non-browser app. val intent = Intent(ACTION_VIEW, Uri.parse(url)).apply { addCategory(CATEGORY_BROWSABLE) flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_REQUIRE_NON_BROWSER or FLAG_ACTIVITY_REQUIRE_DEFAULT } startActivity(intent) } catch (e: ActivityNotFoundException) { // This code executes in one of the following cases: // 1. Only browser apps can handle the intent. // 2. The user has set a browser app as the default app. // 3. The user hasn't set any app as the default for handling this URL. openInCustomTabs(url) }
Java
String url = URL_TO_LOAD; try { // For this intent to be invoked, the system must directly launch a // non-browser app. Intent intent = new Intent(ACTION_VIEW, Uri.parse(url)); intent.addCategory(CATEGORY_BROWSABLE); intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_REQUIRE_NON_BROWSER | FLAG_ACTIVITY_REQUIRE_DEFAULT); startActivity(intent); } catch (ActivityNotFoundException e) { // This code executes in one of the following cases: // 1. Only browser apps can handle the intent. // 2. The user has set a browser app as the default app. // 3. The user hasn't set any app as the default for handling this URL. openInCustomTabs(url); }
打开文件
如果应用需要处理文件或附件,例如需要检查设备能否打开给定文件,通常最简单的办法就是尝试启动可以处理该文件的 activity。为此,使用的 intent 要包含 ACTION_VIEW
intent 操作和代表特定文件的 URI。如果设备上没有可用的应用,您的应用可捕获 ActivityNotFoundException
。在异常处理逻辑中,您可以显示错误或尝试自行处理相关文件。
如果您的应用必须事先知道其他应用能否打开给定文件,请在以下代码段中将 <intent>
元素作为 <queries>
元素的一部分添加到清单中。如果您在编译时已经知道文件类型是什么,请包括文件类型。
<!-- Place inside the <queries> element. --> <intent> <action android:name="android.intent.action.VIEW" /> <!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". --> <data android:mimeType="application/pdf" /> </intent>
然后,您可以使用 intent 调用 resolveActivity()
来检查是否有可用应用。
授予 URI 访问权限
注意:如果应用以 Android 11(API 级别 30)或更高版本为目标平台,则无论其目标 SDK 版本为何,也不论其是否导出自己的 content provider,都必须按照本部分所述声明 URI 访问权限;而且我们也建议对所有应用按照本部分所述声明 URI 访问权限。
为了使以 Android 11 或更高版本为目标平台的应用能够访问内容 URI,该应用的 intent 必须至少设置以下 intent 标志中的一个来声明 URI 访问权限:FLAG_GRANT_READ_URI_PERMISSION
和 FLAG_GRANT_WRITE_URI_PERMISSION
。
在 Android 11 及更高版本中,接收 intent 的应用拥有 URI 访问权限后将具有以下功能:
- 根据给定的 URI 权限针对内容 URI 代表的数据执行读取或写入操作。
- 看到所含 content provider 与 URI 授权相匹配的应用。包含 content provider 的应用可能与发送 intent 的应用不同。
以下代码段演示了如何添加 URI 权限 intent 标志,以便另一个以 Android 11 或更高版本为目标平台的应用可以查看内容 URI 中的数据:
Kotlin
val shareIntent = Intent(Intent.ACTION_VIEW).apply { flags = Intent.FLAG_GRANT_READ_URI_PERMISSION data = CONTENT_URI_TO_SHARE_WITH_OTHER_APP }
Java
Intent shareIntent = new Intent(Intent.ACTION_VIEW); shareIntent.setFlags(FLAG_GRANT_READ_URI_PERMISSION); shareIntent.setData(CONTENT_URI_TO_SHARE_WITH_OTHER_APP);
连接到服务
如果您的应用需要与非自动可见的服务交互,您可以在 <queries>
元素中声明相应的 intent 操作。以下部分提供了使用经常访问的服务的示例。
连接到文字转语音引擎
如果应用需要与文字转语音 (TTS) 引擎交互,请在清单中将以下 <intent>
元素添加为 <queries>
元素的一部分:
<!-- Place inside the <queries> element. --> <intent> <action android:name="android.intent.action.TTS_SERVICE" /> </intent>
连接到语音识别服务
如果您的应用与语音识别服务进行交互,请在清单中将以下 <intent>
元素添加为 <queries>
元素的一部分:
<!-- Place inside the <queries> element. --> <intent> <action android:name="android.speech.RecognitionService" /> </intent>
连接到媒体浏览器服务
如果您的应用是客户端媒体浏览器应用,请在清单的 <queries>
元素中添加以下 <intent>
元素:
<!-- Place inside the <queries> element. --> <intent> <action android:name="android.media.browse.MediaBrowserService" /> </intent>
提供自定义功能
如果您的应用需要执行可自定义操作,或根据您的应用与其他应用的交互情况显示可自定义的信息,您可以在清单中将 intent 过滤器签名作为 <queries>
元素的一部分使用,以表示自定义行为。以下部分针对几种常见场景提供了详细的指导。
查询短信应用
如果您的应用需要了解设备上安装的短信应用集(例如,检查哪个应用是设备的默认短信处理程序),请在清单的 <queries>
元素中添加以下 <intent>
元素:
<!-- Place inside the <queries> element. --> <intent> <action android:name="android.intent.action.SENDTO"/> <data android:scheme="smsto" android:host="*" /> </intent>
创建自定义共享表单
请尽可能使用系统提供的共享表单。或者,您也可以在清单中将以下 <intent>
元素添加为 <queries>
元素的一部分:
<!-- Place inside the <queries> element. --> <intent> <action android:name="android.intent.action.SEND" /> <!-- Replace with the MIME type that your app works with, if needed. --> <data android:mimeType="image/jpeg" /> </intent>
与 Android 11 之前的 Android 版本相比,在应用逻辑中构建 Sharesheet 的过程(例如调用 queryIntentActivities()
)会保持不变。
显示自定义文字选择操作
当用户在您的应用中选择文字时,文字选择工具栏会显示一组可对选定文字执行的操作。如果此工具栏显示来自其他应用的自定义操作,请在清单的 <queries>
元素中添加以下 <intent>
元素:
<!-- Place inside the <queries> element. --> <intent> <action android:name="android.intent.action.PROCESS_TEXT" /> <data android:mimeType="text/plain" /> </intent>
显示联系人的自定义数据行
应用可以向联系人提供程序添加自定义数据行。为了让“通讯录”应用显示此自定义数据,它需要能够执行以下操作:
- 从其他应用读取
contacts.xml
文件。 - 加载与自定义 MIME 类型相对应的图标。
如果您的应用是“通讯录”应用,请在清单中将以下 <intent>
元素添加为 <queries>
元素的一部分:
<!-- Place inside the <queries> element. --> <!-- Lets the app read the contacts.xml file from other apps. --> <intent> <action android:name="android.accounts.AccountAuthenticator" /> </intent> <!-- Lets the app load an icon corresponding to the custom MIME type. --> <intent> <action android:name="android.intent.action.VIEW" /> <data android:scheme="content" android:host="com.android.contacts" android:mimeType="vnd.android.cursor.item/*" /> </intent>