Android中使用ACTION_SENDTO发送邮件近期失效问题求助
我在Android应用中使用Intent.ACTION_SENDTO向指定邮箱发送邮件,以下代码已上线约5年,一直正常运行,可正确填充收件人、主题及邮件正文:
fun sendEmail() { val intent = Intent(Intent.ACTION_SENDTO) intent.data = Uri.parse("mailto:") intent.putExtra(Intent.EXTRA_EMAIL, arrayOf<String>("myemail@email.com")) intent.putExtra(Intent.EXTRA_SUBJECT, "Email Subject here") val emailString = "<b>Email Message here</b>" if (Build.VERSION.SDK_INT >= 24) { intent.putExtra(Intent.EXTRA_TEXT, Html.fromHtml(emailString,Html.FROM_HTML_MODE_LEGACY)) } else { @Suppress("DEPRECATION") intent.putExtra(Intent.EXTRA_TEXT, Html.fromHtml(emailString)) } if (intent.resolveActivity(packageManager) != null) { startActivity(intent) } }
近几周收到多位用户反馈,邮件按钮突然失效,点击后无任何反应。
复现步骤:
在Android Studio模拟器上可复现该问题:intent.resolveActivity(packageManager)返回null,导致startActivity(intent)未执行。若去掉判断直接调用startActivity(intent),模拟器会打开预装的Gmail应用,但这属于不良实践,担心生产环境中无邮件应用的用户会遭遇崩溃。
编辑补充:已在运行Android 14的平板上复现该问题,但运行Android 9的手机上按钮功能正常。推测是近期Android更新导致intent.resolveActivity(packageManager)返回null,请问如何解决该问题?
问题根源
Android 14(API 34)对隐式Intent的解析逻辑做了收紧调整,针对mailto:这类scheme的Intent,部分邮件应用的Intent过滤声明可能未完全符合新规则,导致resolveActivity()返回null,但实际存在可处理该Intent的应用。
具体解决方法
1. 替换resolveActivity为queryIntentActivities检查
使用packageManager.queryIntentActivities()替代resolveActivity(),它能更准确地找到所有可处理该Intent的应用,避免误判:
fun sendEmail() { val intent = Intent(Intent.ACTION_SENDTO) intent.data = Uri.parse("mailto:") intent.putExtra(Intent.EXTRA_EMAIL, arrayOf<String>("myemail@email.com")) intent.putExtra(Intent.EXTRA_SUBJECT, "Email Subject here") val emailString = "<b>Email Message here</b>" if (Build.VERSION.SDK_INT >= 24) { intent.putExtra(Intent.EXTRA_TEXT, Html.fromHtml(emailString,Html.FROM_HTML_MODE_LEGACY)) } else { @Suppress("DEPRECATION") intent.putExtra(Intent.EXTRA_TEXT, Html.fromHtml(emailString)) } // 替换原判断逻辑 val availableActivities = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY) if (availableActivities.isNotEmpty()) { startActivity(intent) } else { // 提示用户无可用邮件应用 Toast.makeText(this, "未找到可用的邮件应用", Toast.LENGTH_SHORT).show() } }
2. 捕获ActivityNotFoundException直接启动
放弃前置检查,直接调用startActivity()并捕获异常,这种方式更简洁,且能覆盖所有情况:
fun sendEmail() { val intent = Intent(Intent.ACTION_SENDTO) intent.data = Uri.parse("mailto:") intent.putExtra(Intent.EXTRA_EMAIL, arrayOf<String>("myemail@email.com")) intent.putExtra(Intent.EXTRA_SUBJECT, "Email Subject here") val emailString = "<b>Email Message here</b>" if (Build.VERSION.SDK_INT >= 24) { intent.putExtra(Intent.EXTRA_TEXT, Html.fromHtml(emailString,Html.FROM_HTML_MODE_LEGACY)) } else { @Suppress("DEPRECATION") intent.putExtra(Intent.EXTRA_TEXT, Html.fromHtml(emailString)) } try { startActivity(intent) } catch (e: ActivityNotFoundException) { Toast.makeText(this, "未找到可用的邮件应用", Toast.LENGTH_SHORT).show() } }
3. 优化Intent构造(可选)
将收件人、主题直接嵌入mailto: Uri中,更符合ACTION_SENDTO的规范,部分邮件应用对这种格式的支持更稳定:
fun sendEmail() { // 直接将收件人、主题编码后放入mailto Uri val subject = Uri.encode("Email Subject here") val intent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:myemail@email.com?subject=$subject")) val emailString = "<b>Email Message here</b>" if (Build.VERSION.SDK_INT >= 24) { intent.putExtra(Intent.EXTRA_TEXT, Html.fromHtml(emailString,Html.FROM_HTML_MODE_LEGACY)) } else { @Suppress("DEPRECATION") intent.putExtra(Intent.EXTRA_TEXT, Html.fromHtml(emailString)) } // 配合上面两种判断方式之一使用 try { startActivity(intent) } catch (e: ActivityNotFoundException) { Toast.makeText(this, "未找到可用的邮件应用", Toast.LENGTH_SHORT).show() } }
内容的提问来源于stack exchange,提问作者Joe

