Android Studio(Kotlin)实现定时邮件发送msg.sentDate不生效如何解决
你设置的msg.sentDate不生效是因为这个属性仅作用于邮件头部的发件时间展示,不会控制邮件的实际发送时机,SMTP协议本身没有延迟发送的能力,只要你调用Transport.send(msg)接口,服务器就会立刻发送邮件。你需要先实现Android端的定时触发逻辑,到指定时间再调用发信接口。
实现方案
一、使用WorkManager实现定时触发(推荐)
WorkManager是Jetpack官方推出的异步任务调度组件,适配从API 14到最新版本的Android系统,支持任务持久化,即使用户重启设备或者应用退出,任务依然可以按预期执行,是后台定时任务的首选方案。
1. 引入依赖
在模块级build.gradle中添加WorkManager依赖:
dependencies { def work_version = "2.8.1" implementation "androidx.work:work-runtime-ktx:$work_version" }
2. 定义发邮件Worker
创建继承自CoroutineWorker的任务类,在doWork方法中执行发信逻辑:
class SendEmailWorker( context: Context, params: WorkerParameters ) : CoroutineWorker(context, params) { override suspend fun doWork(): Result { // 从输入参数中获取邮件相关配置 val smtpServer = inputData.getString("smtp_server") ?: return Result.failure() val smtpPort = inputData.getInt("smtp_port", 587) val fromEmail = inputData.getString("from_email") ?: return Result.failure() val fromPwd = inputData.getString("from_pwd") ?: return Result.failure() val toEmail = inputData.getString("to_email") ?: return Result.failure() val subject = inputData.getString("subject") ?: return Result.failure() val content = inputData.getString("content") ?: return Result.failure() return try { // 初始化BouncyCastle,避免SSL异常 Security.addProvider(BouncyCastleProvider()) val emailService = EmailService(smtpServer, smtpPort) val email = EmailService.Email( auth = EmailService.UserPassAuthenticator(fromEmail, fromPwd), toList = listOf(InternetAddress(toEmail)), from = InternetAddress(fromEmail), subject = subject, body = content ) // IO线程执行发信 withContext(Dispatchers.IO) { emailService.send(email) } Result.success() } catch (e: Exception) { e.printStackTrace() // 失败后重试3次 if (runAttemptCount < 3) Result.retry() else Result.failure() } } }
3. 调度定时任务
用户点击提交按钮时,计算目标时间与当前时间的时间差,构造任务并入队:
// 假设用户选择的目标时间戳为targetTimeMillis(毫秒级) val delayMillis = targetTimeMillis - System.currentTimeMillis() if (delayMillis <= 0) { // 目标时间已过,直接发送 Toast.makeText(this, "目标时间已过,将立即发送邮件", Toast.LENGTH_SHORT).show() } // 构造输入参数 val inputData = workDataOf( "smtp_server" to "smtp.qq.com", // 替换为你的SMTP服务器地址,比如QQ邮箱是smtp.qq.com,网易是smtp.163.com "smtp_port" to 587, // SMTP端口,TLS一般是587,SSL是465 "from_email" to "你的发件邮箱", "from_pwd" to "你的发件邮箱授权码", // 注意不是邮箱密码,是SMTP授权码 "to_email" to 用户输入的收件邮箱, "subject" to 用户输入的邮件主题, "content" to 用户输入的邮件内容 ) // 构造一次性延时任务 val sendRequest = OneTimeWorkRequestBuilder<SendEmailWorker>() .setInitialDelay(delayMillis, TimeUnit.MILLISECONDS) .setInputData(inputData) .setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.MINUTES) // 失败重试策略 .build() // 任务入队 WorkManager.getInstance(this).enqueue(sendRequest)
二、原有发信代码修正
你的EmailService类需要调整以下细节:
class EmailService(private var server: String, private var port: Int) { data class Email( val auth: Authenticator, val toList: List<InternetAddress>, val from: Address, val subject: String, val body: String ) class UserPassAuthenticator( private val username: String, private val password: String ) : Authenticator() { override fun getPasswordAuthentication(): PasswordAuthentication { return PasswordAuthentication(username, password) } } fun send(email: Email) { val props = Properties() props["mail.smtp.auth"] = "true" props["mail.smtp.host"] = server props["mail.smtp.port"] = port.toString() props["mail.smtp.starttls.enable"] = "true" props["mail.smtp.ssl.trust"] = server props["mail.mime.charset"] = "UTF-8" val session = Session.getInstance(props, email.auth) val msg: Message = MimeMessage(session) msg.setFrom(email.from) msg.setRecipients(Message.RecipientType.TO, email.toList.toTypedArray()) msg.replyTo = arrayOf(email.from) msg.addHeader("X-Mailer", CLIENT_NAME) msg.addHeader("Precedence", "bulk") msg.subject = email.subject val multipart = MimeMultipart() val bodyPart = MimeBodyPart() bodyPart.setText(email.body, "UTF-8") // 改为UTF-8避免中文乱码 multipart.addBodyPart(bodyPart) msg.setContent(multipart) Transport.send(msg) } companion object { const val CLIENT_NAME = "Android Automatic Email Client" } }
注意事项
- 国内安卓厂商对后台任务有严格的限制,如果需要任务准时触发,需要引导用户给应用开启后台运行权限、自启动权限
- 发件邮箱需要提前开启SMTP服务,登录邮箱后台即可开启,使用的密码是SMTP授权码不是邮箱登录密码
- Android 13及以上版本需要申请
POST_NOTIFICATIONS权限,WorkManager执行任务时会显示通知 - 不要在客户端硬编码发件邮箱的授权码,避免反编译泄露,如果用户规模较大,建议把发信逻辑放到后端实现,客户端只需要把定时发信请求传给后端即可
内容的提问来源于stack exchange,提问作者ANDROIDDEV
相关产品推荐
相关产品推荐

