Intent.resolveActivity 在 API 30 中返回 null

Coo*_*ind 56 android android-manifest android-intent activitynotfoundexception android-11

查看intent.resolveActivity != null 但启动 Intent 会引发 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()
    }
}
Run Code Online (Sandbox Code Playgroud)

它不起作用。在 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()
    }
}
Run Code Online (Sandbox Code Playgroud)

第一种方法不起作用,因为intent.resolveActivityInfoorintent.resolveActivity返回null。但对于 PDF 浏览器它有效

我们应该解雇intent.resolveActivity吗?

Mik*_* M. 76

这似乎是由于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>
Run Code Online (Sandbox Code Playgroud)

当前可用的信息并不是非常具体,但它确实说明了:

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

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

  • 对我有用,除了有一个警告:“此处不允许元素类别”。 (11认同)
  • 如果抛出某种警告/错误让您知道需要将其添加到清单中,那就太好了 (4认同)
  • 您能解释一下为什么推荐使用 BROWSABLE 类别吗?没有它,事情似乎还是一样的 (2认同)

Coo*_*ind 48

感谢Mike M。我添加了对Browser,Camera和 的查询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>
Run Code Online (Sandbox Code Playgroud)

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

<uses-feature
    android:name="android.hardware.camera"
    android:required="false"
    />
Run Code Online (Sandbox Code Playgroud)

  • @Raii,目前我不知道。我在这两种情况下都使用了“http”。您想知道设备是否有浏览器吗?或者您想通过链接启动您的应用程序?在第二种情况下,您要学习深度链接。 (2认同)
  • @Raii,谢谢!您只能将“https”用于“https://”地址。目前它足以完成您的任务。如果您设置“http”而不是“https”,您将同时打开“https://”和“http://”域。所以,这取决于你,你更喜欢什么。 (2认同)

ful*_*oon 16

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

<queries>
    <intent>
        <action android:name="android.intent.action.SENDTO" />
        <data android:scheme="*" />
    </intent>
</queries>
Run Code Online (Sandbox Code Playgroud)

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

        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)
        }
    }
Run Code Online (Sandbox Code Playgroud)

  • androids官方链接中没有提到这一点,该链接教授如何发送电子邮件https://developer.android.com/guide/components/intents-common#Email。我常常讨厌这个平台,它让我恨不得拔掉自己的头发。 (5认同)
  • 完全同意@muhammad-ahmed-abutalib。对我来说,`&lt;data android:scheme="mailto" /&gt;` 有效。 (3认同)

Avi*_*tal 11

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

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

Run Code Online (Sandbox Code Playgroud)

并写了instaed

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

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


Sta*_*tan 6

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

private fun startIntent(intent: Intent): Boolean {
    return try {
        context.startActivity(intent)
        true
    } catch (e: ActivityNotFoundException) {
        Logger.error("Can't handle intent $intent")
        false
    }
}
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

18215 次

最近记录:

4 年,3 月 前