【问题标题】:intent.resolveActivity returns null in API 30intent.resolveActivity 在 API 30 中返回 null
【发布时间】:2020-10-13 13:54:33
【问题描述】:

看着intent.resolveActivity != null but launching the intent throws an ActivityNotFound exception我写了用深度链接打开浏览器或应用程序:

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 模拟器中找不到浏览器,而常见的 solution 可以工作:

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.resolveActivityInfointent.resolveActivity 返回null。但对于 PDF-viewer,works.

我们应该解雇intent.resolveActivity吗?

【问题讨论】:

  • 假设您的目标是 API 级别 30,这似乎是由于:Package visibility in Android 11。事实上,当我在清单中使用适当的 <queries> 元素测试您的第一个 sn-p 时,它按预期工作。如果您不想包含这样的<queries>,那么您可以坚持使用try-catch
  • @MikeM.,谢谢!您可以将其发布为答案吗?我稍后会测试它。
  • 哦,抱歉,我误读了您的评论。我以为你会在测试后发布它。我现在无法整理出正确的答案,但稍后我会在有空的时候讨论。如果您只是想完成这个问题,请随时自己发布一个,如果您愿意的话。我并不十分担心代表或其他任何事情。 :-) 干杯!
  • 抱歉拖了这么久。我真的很想找到一些与您的具体示例更相关的文档或源代码,但我从来没有这样做过。然后我有点忘记了。我的错。干杯!

标签: android android-intent android-manifest android-11 activitynotfoundexception


【解决方案1】:

这似乎是由于the new restrictions on "package visibility" introduced in Android 11

基本上,从 API 级别 30 开始,如果您以该版本或更高版本为目标,您的应用将无法查看大多数外部包或直接与大多数外部包交互,除非通过一揽子 QUERY_ALL_PACKAGES 许可或通过在清单中包含适当的 <queries> 元素。

确实,您的第一个 sn-p 在该权限下或在清单中使用适当的 <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())根据调用应用程序的&lt;queries&gt; 声明进行过滤

虽然您的示例使用Intent 方法(即resolveActivityInfo()),但实际上在内部调用PackageManager“查询”方法。列出受此更改影响的所有方法和功能的详尽列表可能不可行,但可以肯定的是,如果涉及PackageManager,您最好检查一下新限制下的行为。

【讨论】:

  • 为我工作,但有警告:Element category is not allowed here
  • @Westy92,Google 删除了此警告。
  • 您能解释一下为什么推荐使用 BROWSABLE 类别吗?没有它,事情似乎也一样
  • 如果有某种警告/错误提示你知道你需要在清单中添加它会很好
【解决方案2】:

感谢Mike M. 我添加了对BrowserCameraGallery 的查询。将它们放在AndroidManifest 的任何部分(在&lt;application&gt; 标记之前或之后)。

查看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>

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

<uses-feature
    android:name="android.hardware.camera"
    android:required="false"
    />

【讨论】:

  • 正确答案,谢谢,现在在 Android 11 One plus 设备上运行良好。
  • 它对我有用!!...非常感谢!!
  • @JimmyALejandro,很高兴听到这个消息!祝你好运!
【解决方案3】:

对我来说,我试图发送一封电子邮件,所以我需要像这样在 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)
        }
    }

【讨论】:

  • androids官方链接上没有提到这个,它教如何发送电子邮件developer.android.com/guide/components/intents-common#Email。我经常讨厌这个平台,它让我想把我所有的头发都拉出来。
  • 完全同意 @muhammad-ahmed-abutalib 。对我来说,&lt;data android:scheme="mailto" /&gt; 有效。
【解决方案4】:

对于需要启动Activity的情况(不仅检查是否存在),在this advise之后我删除了

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

然后写了

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

无需在Manifest 处添加任何&lt;queries&gt;。在 Api 28 和 Api 30 上测试。

【讨论】:

  • 这是您必须开始活动的情况。但是可能有这样一种情况,你需要知道是否有应用程序,而不是启动它们。
  • 谢谢!我没有注意到这一点。我编辑答案
  • @CoolMind 你本可以举个例子。如果没有安装应用程序来处理它,我只能想象隐藏一些控件。例如:如果没有相机应用,我不需要照片按钮。
  • @TheincredibleJan,是的。如果你想有一个例子,可能会有你必须选择是启动应用程序还是自己处理的情况。例如,如果该设备中有您公司的注册应用程序,则应用程序将显示一个屏幕,否则将显示通常的屏幕。或者您的应用程序可以通过 Deep Link 打开并编辑电子邮件消息,然后通过电子邮件客户端发送。但是,如果没有电子邮件客户端,例如,它应该打开另一个屏幕或自行关闭。我没有尝试这些方案。
【解决方案5】:

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

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

【讨论】:

    【解决方案6】:

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

    <manifest ... >
        <uses-feature android:name="android.hardware.camera"
                      android:required="true" />
        ...
    </manifest>
    

    【讨论】:

      【解决方案7】:

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

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

      【讨论】:

      • 是的,但是 Google Play restricts 使用了高风险或敏感权限,包括 QUERY_ALL_PACKAGES 权限,该权限可以查看给定设备上已安装应用的清单。
      【解决方案8】:

      如果您使用意图操作ACTION_INSERT,根据文档,您需要将intent-filter 添加到使用操作INSERT 和指定mimeType 的活动中,以拥有intent.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>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-05
        • 1970-01-01
        相关资源
        最近更新 更多