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

每日零点启动ScheduledExecutorService遇阻,求代码校验及替代方案

Let's break down what's wrong with your code first, then fix it, and also share other reliable options for daily midnight tasks on Android.

1. Issues in Your Original Code
  • Syntax Errors: Your code has unmatched parentheses, misplaced parameters (like the random 1500), and messy brace structure. This would prevent the code from compiling entirely.
  • Incorrect Delay Calculation: You’re dividing the time until midnight by 50 and using TimeUnit.MILLISECONDS, which means your initial delay is only 1/50th of the actual needed time—way too short, and potentially negative if it’s already close to midnight.
  • Wrong Repeat Interval: 20*60*60*24 equals 1,728,000 milliseconds (20 days), not 1 day. You need 24*60*60*1000 for a daily repeat.
  • Premature Task Cancellation: You’re scheduling a task to cancel the beeper after 36 milliseconds. That means your scheduled task gets canceled almost immediately—no wonder it never runs!
  • Date Handling Bug: Using set(Calendar.DAY_OF_YEAR, ... +1) will fail on the last day of the year. Use add(Calendar.DAY_OF_YEAR, 1) instead to safely increment the date.
2. Fixed Version of Your Code

Here’s the corrected code that properly schedules a daily task at midnight:

public class MainActivity extends AppCompatActivity {
    ScheduledExecutorService scheduler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        scheduler = Executors.newScheduledThreadPool(1);
        scheduleDailyMidnightTask();
    }

    private void scheduleDailyMidnightTask() {
        // Define the task to run
        final Runnable dailyTask = new Runnable() {
            @Override
            public void run() {
                // Run UI updates on the main thread
                runOnUiThread(() -> {
                    Toast.makeText(getApplicationContext(), "DAILY TASK STARTED", Toast.LENGTH_LONG).show();
                });
            }
        };

        // Calculate delay until next midnight
        Calendar midnight = Calendar.getInstance();
        midnight.set(Calendar.HOUR_OF_DAY, 0);
        midnight.set(Calendar.MINUTE, 0);
        midnight.set(Calendar.SECOND, 0);
        midnight.set(Calendar.MILLISECOND, 0);

        // If current time is already past midnight, schedule for tomorrow
        if (System.currentTimeMillis() >= midnight.getTimeInMillis()) {
            midnight.add(Calendar.DAY_OF_YEAR, 1);
        }

        long initialDelay = midnight.getTimeInMillis() - System.currentTimeMillis();
        long dailyInterval = 24 * 60 * 60 * 1000; // 1 day in milliseconds

        // Schedule the task: initial delay until midnight, then repeat daily
        scheduler.scheduleAtFixedRate(dailyTask, initialDelay, dailyInterval, TimeUnit.MILLISECONDS);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // Shutdown the scheduler to avoid memory leaks
        if (scheduler != null && !scheduler.isShutdown()) {
            scheduler.shutdown();
        }
    }
}

Key Fixes Explained:

  • Fixed all syntax errors and cleaned up the code structure.
  • Corrected the initial delay calculation to use the full time until midnight (no unnecessary division).
  • Set the repeat interval to 1 full day (86,400,000 milliseconds).
  • Removed the premature cancellation task that was killing your job instantly.
  • Added a check to handle if the current time is already past midnight (so it schedules for the next day instead).
  • Added onDestroy() to shut down the scheduler and prevent memory leaks.
3. Better Alternatives for Android Daily Tasks

While ScheduledExecutorService works for simple in-app tasks, it stops working if your app is killed by the system (e.g., low memory). For reliable daily tasks, use these Android-specific solutions:

Option 1: AlarmManager

AlarmManager is designed for system-level alarms, so it will wake up your app (if needed) to run the task, even if the app isn’t active.

Example Code:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MidnightAlarm";
    private PendingIntent pendingIntent;
    private AlarmManager alarmManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setupMidnightAlarm();
    }

    private void setupMidnightAlarm() {
        alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(this, MidnightAlarmReceiver.class);
        pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

        // Calculate next midnight
        Calendar midnight = Calendar.getInstance();
        midnight.set(Calendar.HOUR_OF_DAY, 0);
        midnight.set(Calendar.MINUTE, 0);
        midnight.set(Calendar.SECOND, 0);
        midnight.set(Calendar.MILLISECOND, 0);

        if (System.currentTimeMillis() >= midnight.getTimeInMillis()) {
            midnight.add(Calendar.DAY_OF_YEAR, 1);
        }

        // Set the alarm to repeat daily (adapt to Android versions)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, midnight.getTimeInMillis(), pendingIntent);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            alarmManager.setExact(AlarmManager.RTC_WAKEUP, midnight.getTimeInMillis(), pendingIntent);
        } else {
            alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, midnight.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
        }
    }

    // BroadcastReceiver to handle the alarm trigger
    public static class MidnightAlarmReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            // Run your task here
            Toast.makeText(context, "DAILY ALARM TASK RUNNING", Toast.LENGTH_LONG).show();
            // Use a Handler if you need to run UI code
        }
    }
}

Don’t forget to register the receiver in your AndroidManifest.xml:

<receiver android:name=".MainActivity$MidnightAlarmReceiver" />

WorkManager is part of Android Jetpack and handles background task scheduling with built-in support for system restrictions (like Doze mode). It’s the most reliable way to schedule periodic tasks on modern Android versions.

Step 1: Add Dependency

Add this to your module-level build.gradle:

dependencies {
    implementation "androidx.work:work-runtime:2.8.1"
}

Step 2: Create a Worker Class

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

    @NonNull
    @Override
    public Result doWork() {
        // Your daily task logic here
        Log.d("MidnightWorker", "Daily task executed at midnight");
        // Use LiveData or a broadcast if you need to update the UI
        return Result.success();
    }
}

Step 3: Schedule the Worker in Your Activity

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        scheduleDailyMidnightWork();
    }

    private void scheduleDailyMidnightWork() {
        // Calculate delay until next midnight
        Calendar midnight = Calendar.getInstance();
        midnight.set(Calendar.HOUR_OF_DAY, 0);
        midnight.set(Calendar.MINUTE, 0);
        midnight.set(Calendar.SECOND, 0);
        midnight.set(Calendar.MILLISECOND, 0);

        long initialDelay;
        if (System.currentTimeMillis() >= midnight.getTimeInMillis()) {
            midnight.add(Calendar.DAY_OF_YEAR, 1);
        }
        initialDelay = midnight.getTimeInMillis() - System.currentTimeMillis();

        // Define the work request
        PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(
                MidnightWorker.class,
                1, TimeUnit.DAYS)
                .setInitialDelay(initialDelay, TimeUnit.MILLISECONDS)
                .build();

        // Enqueue the work (replace existing task if it exists)
        WorkManager.getInstance(this).enqueueUniquePeriodicWork(
                "MidnightDailyTask",
                ExistingPeriodicWorkPolicy.REPLACE,
                workRequest);
    }
}

Why WorkManager?

  • Automatically adapts to Android version restrictions (e.g., Doze mode, background limits).
  • Survives app restarts and system reboots (with additional setup for boot completion).
  • Provides built-in retry logic for failed tasks.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:30:25