You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Mail API在Android模拟器可用但真机无法运行的问题排查及解决方案咨询

解决Android真机上JavaMail API连接Gmail SMTP失败的问题

看起来你遇到的是模拟器正常、真机无法连接Gmail SMTP服务器的常见问题,主要和网络权限、Google账户安全设置、Android主线程限制以及代码冗余有关,下面一步步帮你排查解决:

1. 检查网络权限配置

Android真机对权限的管控比模拟器严格,首先确认你的AndroidManifest.xml里已经添加了必要的网络权限:

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

如果你的应用目标是Android 10(API 29)及以上,还要允许明文流量(Gmail 587端口用STARTTLS明文升级加密),在<application>标签里添加:

android:usesCleartextTraffic="true"

另外,确保真机的系统设置没有禁用该应用的网络访问权限。

2. 修复Gmail账户的安全设置

这是最核心的原因,Google现在对普通密码登录SMTP做了严格限制:

  • 如果账户开启了两步验证:必须使用**应用专用密码(App Password)**代替原登录密码。登录你的Google账户安全页面,找到「应用专用密码」,创建一个用于邮件客户端的密码,替换代码里的Config.PASSWORD。
  • 如果未开启两步验证:Google已经移除了「允许不太安全的应用」选项,你要么开启两步验证使用应用专用密码,要么改用OAuth2认证(后面会提到Gmail API)。
    同时,确保Gmail网页版的「设置→转发和POP/IMAP」中已经勾选「启用IMAP」。

3. 必须在子线程中执行邮件发送

Android 4.0之后主线程禁止执行网络操作,模拟器可能没有严格检测,但真机一定会抛出NetworkOnMainThreadException,导致连接失败。你的sendMail是同步方法,必须放在子线程调用,示例代码如下:

new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            GMailSender sender = new GMailSender(Config.EMAIL, Config.PASSWORD);
            sender.sendMail("测试主题", "测试内容", Config.EMAIL, "收件人邮箱");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}).start();

4. 优化JavaMail配置与代码冗余

你的代码存在两个Authenticator逻辑冗余的问题,可能导致认证信息不一致,建议简化:

public class GMailSender extends javax.mail.Authenticator {
    private Session session;
    private String user;
    private String password;

    static {
        Security.addProvider(new JSSEProvider());
    }

    public GMailSender(String user, String password) {
        this.user = user;
        this.password = password;
        Properties properties = new Properties();
        properties.put("mail.smtp.host", Config.MAIL_HOST);
        properties.put("mail.smtp.port", Config.MAIL_PORT);
        properties.put("mail.smtp.auth", "true");
        properties.put("mail.smtp.starttls.enable", "true");
        properties.put("mail.smtp.ssl.trust", "smtp.gmail.com"); // 信任SMTP主机
        properties.put("mail.smtp.connectiontimeout", "5000"); // 连接超时配置
        // 直接传入当前类(已继承Authenticator),无需匿名类
        session = Session.getInstance(properties, this);
    }

    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication(user, password);
    }

    public synchronized void sendMail(String subject, String body, String sender, String recipients) throws Exception {
        MimeMessage message = new MimeMessage(session);
        DataHandler handler = new DataHandler(new ByteArrayDataSource(body.getBytes(), "text/html"));
        message.setSender(new InternetAddress(sender));
        message.setSubject(subject);
        message.setDataHandler(handler);
        if (recipients.indexOf(',') > 0)
            message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(recipients));
        else
            message.setRecipient(Message.RecipientType.TO, new InternetAddress(recipients));
        Transport.send(message);
    }
}

同时确保你的JSSEProvider类实现完整,用于提供加密算法支持:

public class JSSEProvider extends Provider {
    public JSSEProvider() {
        super("HarmonyJSSE", 1.0, "Harmony JSSE Provider");
        put("SSLContext.TLS",
                "org.apache.harmony.xnet.provider.jsse.SSLContextImpl");
        put("Alg.Alias.SSLContext.TLSv1", "TLS");
        put("KeyManagerFactory.X509",
                "org.apache.harmony.xnet.provider.jsse.KeyManagerFactoryImpl");
        put("TrustManagerFactory.X509",
                "org.apache.harmony.xnet.provider.jsse.TrustManagerFactoryImpl");
    }
}

是否应该改用Gmail API?

非常推荐!JavaMail API依赖账户密码的方式已经不符合Google的安全政策,未来可能会被完全禁止。Gmail API使用OAuth2认证,不需要暴露用户密码,是官方支持的安全方案,更适合长期维护的应用。你可以通过官方文档集成Gmail API实现邮件发送功能。

内容的提问来源于stack exchange,提问作者Leari John Barasona

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 07:27:46