You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何用Android WorkManager在网络稳定时重发未成功发送的邮件?

解决WorkManager调度邮件重发的问题

看起来你已经找对了方向——用WorkManager来处理网络恢复后的邮件补发,但当前代码存在几个关键问题需要调整:比如Worker里直接调用和UI绑定的sendPortraitImage(里面有UI操作,Worker是后台线程不能操作UI)、没有持久化未发送任务的必要信息、邮件发送逻辑和UI逻辑耦合在一起。下面是具体的解决方案:

核心思路拆解

  1. 分离UI与业务逻辑:把邮件发送的核心逻辑从sendPortraitImage中抽离出来,避免Worker触碰UI操作
  2. 持久化未发送任务:将邮件所需的参数(图片路径、收件人、主题等)保存到本地,Worker从本地读取这些信息执行发送
  3. 改造Worker执行逻辑:让Worker读取本地存储的待发送任务,调用独立的邮件发送方法,并处理发送结果(成功则删除记录,失败则触发重试)

具体代码实现

第一步:抽离邮件发送核心逻辑到工具类

创建一个独立的邮件发送工具类,用同步方式执行发送(因为Worker的doWork是同步线程,不能用AsyncTask):

public class EmailSender {
    // 同步发送邮件的方法,返回发送结果
    public static boolean sendEmail(Context context, String recipient, String subject, String message, String imagePath) {
        try {
            // 这里实现你的邮件发送逻辑:
            // 1. 构建邮件内容(包含图片附件)
            // 2. 使用JavaMail或你的自定义mail类的同步版本执行发送
            // 注意:替换成你原来mail.execute()里的核心代码,改成同步调用
            
            // 示例伪代码:
            MailSync mailSync = new MailSync(context, recipient, subject, message, imagePath);
            return mailSync.sendSynchronously();
            
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

第二步:改造sendPortraitImage方法

分离UI操作和邮件发送逻辑,发送失败时持久化任务并调度WorkManager:

public void sendPortraitImage(View vw){
    // --- UI操作部分(仅在Activity中执行)---
    send.setVisibility(View.GONE);
    buttonCamera.setVisibility(View.GONE);
    bitmap_view = ScreenShott.getInstance().takeScreenShotOfRootView(vw, null);
    saveImageToExternalStorage(bitmap_view);
    Toast.makeText(getApplicationContext(), "Saved successfully, Check gallery", Toast.LENGTH_SHORT).show();

    // --- 邮件参数准备 ---
    String email = email_auth.EMAIL_FROM;
    String sub = TypeHeader + " - " + TypeNo + " - " + ImageNo;
    String msg = "Test AlmaPix";
    String fileLoc = file.getAbsolutePath();

    // --- 尝试立即发送,失败则保存任务并调度WorkManager ---
    boolean sendSuccess = EmailSender.sendEmail(this, email, sub, msg, fileLoc);
    if (!sendSuccess) {
        // 用SharedPreferences持久化任务(如果有多任务建议用Room数据库)
        savePendingEmailTask(email, sub, msg, fileLoc);
        // 调度WorkManager
        scheduleEmailSync();
    }
}

// 保存待发送任务到本地
private void savePendingEmailTask(String email, String subject, String message, String imagePath) {
    SharedPreferences pendingPrefs = getSharedPreferences("pending_emails", MODE_PRIVATE);
    // 用时间戳作为唯一键,避免重复
    String taskKey = "pending_" + System.currentTimeMillis();
    Gson gson = new Gson();
    PendingEmailTask task = new PendingEmailTask(email, subject, message, imagePath);
    pendingPrefs.edit().putString(taskKey, gson.toJson(task)).apply();
}

// 定义任务实体类,用于序列化存储
static class PendingEmailTask {
    String email;
    String subject;
    String message;
    String imagePath;

    public PendingEmailTask(String email, String subject, String message, String imagePath) {
        this.email = email;
        this.subject = subject;
        this.message = message;
        this.imagePath = imagePath;
    }

    // 空构造函数用于Gson反序列化
    public PendingEmailTask() {}
}

第三步:改造WorkManager调度方法

设置合理的约束和重试策略:

private void scheduleEmailSync() {
    // 设置网络约束:仅当网络连接时执行
    Constraints constraints = new Constraints.Builder()
            .setRequiredNetworkType(NetworkType.CONNECTED)
            .build();

    // 设置重试策略:指数退避,避免频繁重试
    OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyWorker.class)
            .setConstraints(constraints)
            .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 
                               OneTimeWorkRequest.MIN_BACKOFF_MILLIS, 
                               TimeUnit.MILLISECONDS)
            .build();

    WorkManager.getInstance(this).enqueue(workRequest);
}

第四步:改造MyWorker类

让Worker读取本地待发送任务,执行发送并处理结果:

public class MyWorker extends Worker {
    public MyWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    @NonNull
    @Override
    public Result doWork() {
        SharedPreferences pendingPrefs = getApplicationContext().getSharedPreferences("pending_emails", MODE_PRIVATE);
        Map<String, ?> pendingTasks = pendingPrefs.getAll();
        Gson gson = new Gson();
        boolean allTasksSuccess = true;

        // 遍历所有待发送任务
        for (Map.Entry<String, ?> entry : pendingTasks.entrySet()) {
            String taskJson = (String) entry.getValue();
            PendingEmailTask task = gson.fromJson(taskJson, PendingEmailTask.class);

            // 执行邮件发送
            boolean sendSuccess = EmailSender.sendEmail(getApplicationContext(), 
                                                       task.email, 
                                                       task.subject, 
                                                       task.message, 
                                                       task.imagePath);
            if (sendSuccess) {
                // 发送成功,删除本地任务记录
                pendingPrefs.edit().remove(entry.getKey()).apply();
            } else {
                allTasksSuccess = false;
                // 发送失败,保留任务等待下次重试
            }
        }

        // 发送通知告知用户结果
        if (allTasksSuccess) {
            displayNotification("邮件同步完成", "所有未发送的邮件已成功发送");
            return Result.SUCCESS;
        } else {
            displayNotification("邮件同步失败", "部分邮件发送失败,将在网络恢复后重试");
            return Result.RETRY;
        }
    }

    private void displayNotification(String title, String content) {
        NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel("email_sync", "邮件同步", NotificationManager.IMPORTANCE_DEFAULT);
            notificationManager.createNotificationChannel(channel);
        }
        NotificationCompat.Builder notification = new NotificationCompat.Builder(getApplicationContext(), "email_sync")
                .setContentTitle(title)
                .setContentText(content)
                .setSmallIcon(R.mipmap.ic_launcher);
        notificationManager.notify(2, notification.build());
    }

    // 内部静态类,用于反序列化任务
    static class PendingEmailTask {
        String email;
        String subject;
        String message;
        String imagePath;

        public PendingEmailTask() {}
    }
}

关键注意事项

  1. 替换邮件发送逻辑EmailSender里的sendSynchronously需要你实现同步的邮件发送逻辑,因为Worker不能用AsyncTask(Android 11+已废弃)。
  2. 多任务场景优化:如果有大量待发送任务,建议用Room数据库替代SharedPreferences,更适合存储结构化数据。
  3. 权限处理:确保应用拥有INTERNETREAD_EXTERNAL_STORAGE(如果图片存在外部存储)等权限,Android 13+还需要处理POST_NOTIFICATIONS权限。
  4. 图片文件持久化:确保图片文件在Worker运行时仍然存在,建议将图片保存到应用私有目录,避免被系统清理。

内容的提问来源于stack exchange,提问作者TheAlmac2

火山引擎 最新活动