intent.resolveActivity 在 API 30 中返回 null



查看 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()
}
}

它不起作用。在 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.resolveActivityInfointent.resolveActivity返回null。但是对于PDF查看器,它可以工作。

我们应该解雇intent.resolveActivity吗?

这似乎是由于 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>

目前可用的信息不是很具体,但它确实指出:

返回有关其他应用(如queryIntentActivities()(的结果的PackageManager方法根据调用应用的<queries>声明进行筛选

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

感谢迈克·我添加了对BrowserCameraGallery的查询。将它们放在AndroidManifest内的任何部分(在标签之前或之后<application>(。

看着MediaStore.ACTION_IMAGE_CAPTURE和Intent.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"
/>

对我来说,我正在尝试发送电子邮件,所以我需要在清单中设置查询,如下所示:

<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)
}
}

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

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

并写了 instaed

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

无需在Manifest添加任何<queries>。在 Api 28 和 API 30 上都经过测试。

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

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

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

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

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

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

我的解决方案

将此添加到清单

<queries>
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
</queries>

使用此函数

private File create_image() throws IOException {
@SuppressLint("SimpleDateFormat") String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "img_" + timeStamp;
File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
return new File(storageDir, imageFileName + ".jpg");
}

将此用于安卓 10

<application
android:requestLegacyExternalStorage="true"
...
</application>

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

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

由于用户提出的问题没有设置任何限制,因此您可以简单地这样做,它将使用您在 INTNET 中传递的电子邮件 ID 直接导航到 GMAIL

val emailTo = "user@gmail.com" // just ex. write the email address you want
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("mailto:$emailTo")
startActivity(intent) // <---- this is work for activity and fragment both

替换

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()
}

if (requireActivity().packageManager.resolveActivity(intent,0) != null){
startActivity(intent)
}else{
Toast.makeText(
this,
"No application can handle the link",
Toast.LENGTH_SHORT
).show()
}

相关内容

  • 没有找到相关文章

最新更新