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
相关产品推荐
相关产品推荐

