90

查看intent.resolveActivity != null 但启动意图会引发 ActivityNotFound 异常,我写的是打开浏览器或具有深度链接的应用程序:

private fun openUrl(url: String) {
    val intent = Intent().apply {
        action = Intent.ACTION_VIEW
        data = Uri.parse(url)
//        setDataAndType(Uri.parse(url), "text/html")
//        component = ComponentName("com.android.browser", "com.android.browser.BrowserActivity")
//        flags = Intent.FLAG_ACTIVITY_CLEAR_TOP + Intent.FLAG_GRANT_READ_URI_PERMISSION
    }
    val activityInfo = intent.resolveActivityInfo(packageManager, intent.flags)
    if (activityInfo?.exported == true) {
        startActivity(intent)
    } else {
        Toast.makeText(
            this,
            "No application can handle the link",
            Toast.LENGTH_SHORT
        ).show()
    }
}

它不起作用。在 API 30 模拟器中找不到浏览器,而通用解决方案有效:

private fun openUrl(url: String) {
    val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
    try {
        startActivity(intent)
    } catch (e: ActivityNotFoundException) {
        Toast.makeText(
            this,
            "No application can handle the link",
            Toast.LENGTH_SHORT
        ).show()
    }
}

第一种方法不起作用,因为intent.resolveActivityInfoorintent.resolveActivity返回null。但是对于 PDF 查看器,它可以工作

我们应该解雇intent.resolveActivity吗?

4

8 回答 8

115

这似乎是由于Android 11 中引入了对“包可见性”的新限制

基本上,从 API 级别 30 开始,如果您的目标是该版本或更高版本,您的应用程序无法看到或直接与大多数外部包交互,除非通过一揽子QUERY_ALL_PACKAGES许可或<queries>在您的显现。

<queries>实际上,您的第一个片段在该权限或清单中的适当元素下按预期工作;例如:

<queries>
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="https" />
    </intent>
</queries>

当前可用的信息并不是非常具体,但确实指出:

返回有关其他应用程序的结果的PackageManager方法,例如queryIntentActivities(),根据调用应用程序的<queries>声明进行过滤

尽管您的示例使用的是一种Intent方法——即,resolveActivityInfo()它实际上是在PackageManager内部调用“查询”方法。受此更改影响的每个方法和功能的详尽列表可能不可行,但可以安全地假设如果PackageManager涉及,您最好检查一下新限制的行为。

于 2020-07-12T03:24:27.993 回答
76

感谢Mike M。我添加了对BrowserCamera的查询Gallery。将它们放在其中AndroidManifest的任何部分(<application>标签之前或之后)。

查看MediaStore.ACTION_IMAGE_CAPTUREIntent.ACTION_GET_CONTENT我得到了这两个动作。

<queries>
    <!-- Browser -->
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <data android:scheme="http" />
    </intent>

    <!-- Camera -->
    <intent>
        <action android:name="android.media.action.IMAGE_CAPTURE" />
    </intent>

    <!-- Gallery -->
    <intent>
        <action android:name="android.intent.action.GET_CONTENT" />
    </intent>
</queries>

我不需要迪伦的回答,所以我仍然有

<uses-feature
    android:name="android.hardware.camera"
    android:required="false"
    />
于 2020-10-05T08:03:13.520 回答
28

对我来说,我试图发送一封电子邮件,所以我需要像这样在 Manifest 中设置查询:

<queries>
    <intent>
        <action android:name="android.intent.action.SENDTO" />
        <data android:scheme="*" />
    </intent>
</queries>

然后发送电子邮件并检查电子邮件客户端,如下所示:

        private fun sendEmail(to: Array<String>) {
        val intent = Intent(Intent.ACTION_SENDTO)
        intent.data = Uri.parse("mailto:") // only email apps should handle this
        intent.putExtra(Intent.EXTRA_EMAIL, to)
//        intent.putExtra(Intent.EXTRA_SUBJECT, subject)
        if (intent.resolveActivity(requireContext().packageManager) != null) {
            startActivity(intent)
        }
    }
于 2020-12-06T08:21:21.100 回答
21

对于需要启动Activity(不仅检查是否存在)的情况,请按照此建议删除

if (intent.resolveActivity(packageManager) != null)
                startActivity(intent);

并写道

try {
     startActivity(intent);
} catch (ActivityNotFoundException e) {
     Toast.makeText(getContext(), getString(R.string.error), Toast.LENGTH_SHORT).show();
}

无需<queries>Manifest. 在 Api 28 和 Api 30 上测试。

于 2021-01-21T08:33:01.037 回答
4

要迭代 Avital 的答案,如果您想启动一个意图并了解它是否已启动,则无需声明任何内容:

private fun startIntent(intent: Intent): Boolean {
    return try {
        context.startActivity(intent)
        true
    } catch (e: ActivityNotFoundException) {
        Logger.error("Can't handle intent $intent")
        false
    }
}
于 2021-06-17T07:59:30.283 回答
1

除了下面的代码之外,迈克所说的对我有用。

<manifest ... >
    <uses-feature android:name="android.hardware.camera"
                  android:required="true" />
    ...
</manifest>
于 2020-09-29T14:22:34.850 回答
1

您可以在 AndroidManifest 中添加 QUERY_ALL_PACKAGES 权限。它不需要运行时权限请求。

<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
于 2021-05-27T09:34:10.283 回答
0

如果您正在使用意图操作ACTION_INSERT,根据文档,您需要将与操作和指定intent-filter的操作一起添加到使用活动中,以返回 true。INSERTmimeTypeintent.resolveActivity(view.context.packageManager) != null

<activity ...>
<intent-filter>
    <action android:name="android.intent.action.INSERT" />
    <data android:mimeType="vnd.android.cursor.dir/event" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>
于 2022-01-20T23:03:00.817 回答