如何从我的Android应用程序发送电子邮件?

555

我正在开发一款安卓应用程序。我不知道如何从应用程序中发送电子邮件?


简单的ShareBuilder https://gist.github.com/gelldur/9c199654c91b13478979 - Gelldur
这个回答解决了你的问题吗?Android Studio mailto Intent doesn't show subject and mail body - Alex
建议的重复似乎更糟糕,被接受的答案有一个奇怪、不必要的意图过滤器。 - Ryan M
26个回答

1018

最好(也是最简单)的方法是使用一个 Intent

Intent i = new Intent(Intent.ACTION_SEND);
i.setType("message/rfc822");
i.putExtra(Intent.EXTRA_EMAIL  , new String[]{"recipient@example.com"});
i.putExtra(Intent.EXTRA_SUBJECT, "subject of email");
i.putExtra(Intent.EXTRA_TEXT   , "body of email");
try {
    startActivity(Intent.createChooser(i, "Send mail..."));
} catch (android.content.ActivityNotFoundException ex) {
    Toast.makeText(MyActivity.this, "There are no email clients installed.", Toast.LENGTH_SHORT).show();
}

否则你将不得不编写自己的客户端。
RFC822是ARPA互联网文本消息的标准。请参见https://w3.org/Protocols/rfc822

7
在上述代码中,没有发件人电子邮件地址。那么邮件是如何发送的呢? - KIRAN K J
40
你需要了解意图的工作原理才能理解这个。它基本上会打开一个邮件应用程序,并自动填写收件人、主题和正文。发送邮件取决于邮件应用程序。 - Jeremy Logan
8
开始活动时,电子邮件在“收件人”字段中未出现。有人知道原因吗? - Hamza Waqas
12
这条评论的重要贡献是:message/rfc822。 - Guilherme Oliveira
28
为确保选择器仅显示电子邮件应用程序,请添加以下内容:Intent i = new Intent(Intent.ACTION_SENDTO); i.setType("message/rfc822"); i.setData(Uri.parse("mailto:")); - Edris
显示剩余16条评论

206

如果不使用 .setType("message/rfc822"),那么选择器将显示所有支持发送intent的应用程序(非常多)。


5
很好,说实话这个应该有更多点赞。你在模拟器上测试时可能不会注意到,但是当你在真实设备上尝试发送"text/plain"时,它会提供给你一个包含15个以上应用程序的列表!!因此,“message/rfc822”绝对是推荐的(电子邮件标准)。 - Blundell
8
你好,@Blundell。但是在更改为message/rfc822后,我没有看到任何区别。 - draw
2
你能从列表中删除蓝牙吗?这个也会显示出来。虽然如此,还是很棒的技巧! - ingh.am
6
拯救了我们的麻烦。无法想象要向客户解释用户可能会通过推特发送支持请求而不是邮件。 - Kevin Galligan
1
+1111111 这个问题值得无数个 +1,这样其他人也能看到。我错过了这一部分,不得不花一段时间来解决这个问题! - ChallengeAccepted
显示剩余2条评论

99

我之前一直在使用这个方法,效果不错,没有出现非电子邮件类型的应用程序。这只是发送电子邮件意图的另一种方式:

Intent intent = new Intent(Intent.ACTION_SENDTO); // it's not ACTION_SEND
intent.putExtra(Intent.EXTRA_SUBJECT, "Subject of email");
intent.putExtra(Intent.EXTRA_TEXT, "Body of email");
intent.setData(Uri.parse("mailto:default@example.com")); // or just "mailto:" for blank
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // this will make such that when user returns to your app, your app is displayed, instead of the email app.
startActivity(intent);

2
不支持的操作:目前不支持此操作。 - erdomester
4
请将 "setType("(plain/text")" 更改为 "setType("text/plain")"。 - sachit
2
.setType("message/rfc822") 而不是 text/plain - The Hungry Androider
1
这段代码将打开电子邮件意图?我如何在不向用户显示意图的情况下发送电子邮件@yuku,我想将密码发送到电子邮件中。 - Erum
2
这个答案非常有影响力。 :) - Nick Volynkin
显示剩余11条评论

58

我曾使用与当前被接受的答案类似的方法来发送附加有二进制错误日志文件的电子邮件。Gmail和K-9可以成功发送并且在我的邮件服务器上也收到了。唯一的问题是我的邮件客户端Thunderbird,它无法打开/保存附加的日志文件。事实上,它根本没有任何投诉就没有保存该文件。

我查看了其中一封邮件的源代码并注意到日志文件附件有一个(可以理解的)mime类型message/rfc822。当然,那个附件不是一封附加的电子邮件。但是Thunderbird不能优雅地处理这个小错误。所以这有点糟糕。

经过一些研究和实验,我想出了以下解决方案:

public Intent createEmailOnlyChooserIntent(Intent source,
    CharSequence chooserTitle) {
    Stack<Intent> intents = new Stack<Intent>();
    Intent i = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto",
            "info@example.com", null));
    List<ResolveInfo> activities = getPackageManager()
            .queryIntentActivities(i, 0);

    for(ResolveInfo ri : activities) {
        Intent target = new Intent(source);
        target.setPackage(ri.activityInfo.packageName);
        intents.add(target);
    }

    if(!intents.isEmpty()) {
        Intent chooserIntent = Intent.createChooser(intents.remove(0),
                chooserTitle);
        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,
                intents.toArray(new Parcelable[intents.size()]));

        return chooserIntent;
    } else {
        return Intent.createChooser(source, chooserTitle);
    }
}

可以按以下方式使用:

Intent i = new Intent(Intent.ACTION_SEND);
i.setType("*/*");
i.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(crashLogFile));
i.putExtra(Intent.EXTRA_EMAIL, new String[] {
    ANDROID_SUPPORT_EMAIL
});
i.putExtra(Intent.EXTRA_SUBJECT, "Crash report");
i.putExtra(Intent.EXTRA_TEXT, "Some crash report details");

startActivity(createEmailOnlyChooserIntent(i, "Send via email"));

你可以看到,createEmailOnlyChooserIntent方法很容易通过正确的intent和正确的mime类型进行调用。

它接着会遍历所有可响应ACTION_SENDTO协议的intent(即只有电子邮件应用程序才会响应),并基于这些活动和原始的ACTION_SEND意图及正确的mime类型构建一个chooser。

另一个优点是Skype不再列出(因为它响应rfc822 mime类型)。


1
我刚刚插入了你的代码片段,它运行得很好。之前列出了像Google Drive、Skype等应用程序。但是难道没有一种方法可以在不调用其他应用程序的情况下从应用程序中发送邮件吗?我刚刚阅读了@Rene上面发布的关于电子邮件客户端的文章,但似乎对于只是发送简单电子邮件来说太过复杂了。 - Alex Cio
非常好的答案。我也试过使用Skype和Google Drive来实现ACTION_SEND,但这个方法解决了我的问题。 - darrenp
1
最受欢迎的解决方案还包括Skype和Vkontakte。这个解决方案更好。 - Denis Kutlubaev
什么是crashLogFile?它在哪里初始化了?请具体说明。 - Noufal
@Noufal 这只是我自己代码库中的一些剩余部分。它是一个指向崩溃日志文件的File实例,我的Android应用程序在后台创建该文件以防止出现未捕获的异常。这个例子只是说明如何添加电子邮件附件。您还可以附加来自外部存储的任何其他文件(例如图像)。您也可以删除带有crashLogFile的行,以获取可工作的示例。 - tiguchi
显示剩余2条评论

39

若要让电子邮件应用程序解决您的意图,您需要将ACTION_SENDTO指定为操作,并将mailto指定为数据。

private void sendEmail(){

    Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
    emailIntent.setData(Uri.parse("mailto:" + "recipient@example.com")); // You can use "mailto:" if you don't know the address beforehand.
    emailIntent.putExtra(Intent.EXTRA_SUBJECT, "My email's subject");
    emailIntent.putExtra(Intent.EXTRA_TEXT, "My email's body");
    
    try {
        startActivity(Intent.createChooser(emailIntent, "Send email using..."));
    } catch (android.content.ActivityNotFoundException ex) {
        Toast.makeText(Activity.this, "No email clients installed.", Toast.LENGTH_SHORT).show();
    }

}

29

解决方法很简单:Android文档已经对此进行了说明。

(https://developer.android.com/guide/components/intents-common.html#Email)

最重要的是标志:它是ACTION_SENDTO,而不是ACTION_SEND

另一行重要的内容是:

intent.setData(Uri.parse("mailto:")); ***// only email apps should handle this***

顺便提一下,如果您发送了一个空的Extra,那么末尾的if()将不起作用,应用程序将无法启动电子邮件客户端。

根据Android文档。如果您想确保意图仅由电子邮件应用程序处理(而不是其他短信或社交应用程序),则使用ACTION_SENDTO操作并包含“mailto:”数据方案。例如:

public void composeEmail(String[] addresses, String subject) {
    Intent intent = new Intent(Intent.ACTION_SENDTO);
    intent.setData(Uri.parse("mailto:")); // only email apps should handle this
    intent.putExtra(Intent.EXTRA_EMAIL, addresses);
    intent.putExtra(Intent.EXTRA_SUBJECT, subject);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

23
使用 .setType("message/rfc822") 或者 ACTION_SEND 的策略似乎也会匹配非电子邮件客户端的应用程序,比如 Android BeamBluetooth
使用 ACTION_SENDTOmailto: URI 似乎可以完美地工作,并且在开发者文档中推荐使用。但是,在官方模拟器上执行此操作且没有设置任何电子邮件帐户(或没有邮件客户端)时,您将收到以下错误:

不支持的操作

当前不支持该操作。

如下所示:

Unsupported action: That action is not currently supported.

原来模拟器将意图解析为名为com.android.fallback.Fallback的活动,该活动显示上述消息。 显然这是设计如此。 如果您希望您的应用程序绕过此问题,以便在官方模拟器上正常工作,您可以在尝试发送电子邮件之前检查它:
private void sendEmail() {
    Intent intent = new Intent(Intent.ACTION_SENDTO)
        .setData(new Uri.Builder().scheme("mailto").build())
        .putExtra(Intent.EXTRA_EMAIL, new String[]{ "John Smith <johnsmith@yourdomain.com>" })
        .putExtra(Intent.EXTRA_SUBJECT, "Email subject")
        .putExtra(Intent.EXTRA_TEXT, "Email body")
    ;

    ComponentName emailApp = intent.resolveActivity(getPackageManager());
    ComponentName unsupportedAction = ComponentName.unflattenFromString("com.android.fallback/.Fallback");
    if (emailApp != null && !emailApp.equals(unsupportedAction))
        try {
            // Needed to customise the chooser dialog title since it might default to "Share with"
            // Note that the chooser will still be skipped if only one app is matched
            Intent chooser = Intent.createChooser(intent, "Send email with");
            startActivity(chooser);
            return;
        }
        catch (ActivityNotFoundException ignored) {
        }

    Toast
        .makeText(this, "Couldn't find an email app and account", Toast.LENGTH_LONG)
        .show();
}

开发者文档中查找更多信息。


13

发送电子邮件可以使用意图(Intents)完成,无需配置。但这样做需要用户交互,而且布局会有些受限。

如果要构建和发送更复杂的电子邮件且不需要用户交互,则需要构建自己的客户端。首先要知道的是,用于电子邮件的Sun Java API不可用。我成功地利用了Apache Mime4j库来构建电子邮件。所有基于这里的文档。


7

以下是示例工作代码,可在Android设备上打开邮件应用程序,并自动填写收件人地址主题以进行撰写邮件。

protected void sendEmail() {
    Intent intent = new Intent(Intent.ACTION_SENDTO);
    intent.setData(Uri.parse("mailto:feedback@gmail.com"));
    intent.putExtra(Intent.EXTRA_SUBJECT, "Feedback");
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
}

谢谢。与@Avi Parshan的解决方案相比,您在setData()中设置了电子邮件,而Avi则在putExtra()中设置了电子邮件。两种变体都可以工作。但是,如果删除setData并仅使用intent.putExtra(Intent.EXTRA_EMAIL, new String[]{"hi@example.com"});,将会出现ActivityNotFoundException - CoolMind

5

我在我的应用程序中使用以下代码。这将显示电子邮件客户端应用程序,例如 Gmail。

    Intent contactIntent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", getString(R.string.email_to), null));
    contactIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.email_subject));
    startActivity(Intent.createChooser(contactIntent, getString(R.string.email_chooser)));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接