1. Project introduction
1. Background and meaning
In Android applications, the requirements for scheduled tasks are almost everywhere: from timed refreshing data, timed backups, timed push notifications, to silent downloads at night, and looping execution of certain business logic, the system requires the system to trigger code execution at a specified time or interval. Due to the Android system's own life cycle management, Doze mode, power optimization and other mechanisms, the implementation of timing tasks must not only ensure accuracy, but also take into account both power saving and resource utilization. Therefore, several common implementation methods have their own focus and usage scenarios.
This article will comprehensively sort out the main solutions for implementing timing tasks on Android from multiple dimensions such as principles, best practices, advantages and limitations, and supplement it with complete and runnable sample code. The structure of this article is as follows:
Common scenarios and requirements for timing tasks
Related basic knowledge and constraints
Plan 1:
()
andRunnable
Plan 2:
Timer
/TimerTask
Plan 3:
ScheduledThreadPoolExecutor
Plan 4:
AlarmManager
Plan 5:
JobScheduler
Plan 6:
WorkManager
Plan 7: Front Desk Service (
Service
+Handler
/AlarmManager
)Environment and dependency
Complete code integration (a block of code, separated files with comments)
Solution comparison and selection suggestions
Performance and power saving optimization
Project summary and expansion ideas
FAQ
2. Related basic knowledge and system constraints
-
Main thread and child thread
Handler
: On the main thread or the specified threadLooper
Up scheduleRunnable
;TimerTask
/ScheduledThreadPoolExecutor
: Execute timed tasks in the background thread pool, pay attention to the life cycle.
-
System power saving mechanism
Doze mode(Android 6.0+) will delay or batch processing to wake up regularly;
App Standby、Battery SaverWill restrict background scheduling;
-
Process and component life cycle
The process is recycled,
Service
Destroy, the timing needs to be persisted or coordinated with the system scheduler;
-
Accuracy and power consumption
High frequency and high precision wake-up will consume a lot of power;
The application scenario determines what precision and scheduler to use;
-
Cross-restart and persistence
AlarmManager
It can be set to take effect after the device restarts (needs to register dynamically or statically.BOOT_COMPLETED
);JobScheduler
andWorkManager
It can be automatically restored after restart.
3. Plan 1: ()
3.1 Principle
Handler
By binding to itLooper
(usually the main thread) sends a delay message and executesRunnable
. Commonly usedShort time、Low frequency, timing operations that interact closely with the UI.
3.2 Sample code
// Used in Activity or Serviceprivate val handler = Handler(()) private val task = object : Runnable { override fun run() { // Execute timing tasks refreshUI() // Scheduling again (this, 5000) } } override fun onStart() { () (task, 5000) // Execute for the first time in 5 seconds} override fun onStop() { () (task) // Stop scheduling}
3.3 Pros and cons
advantage: Easy to use, easy to execute custom logic;
shortcoming: Relying on the process to survive, the process is dead or the device is sleeping, and the execution cannot be guaranteed; high-frequency scheduling consumes power;
4. Plan 2: Timer/TimerTask
4.1 Principle
Scheduled one or more in a separate thread
TimerTask
,based on, suitable for simple background timing.
4.2 Sample code
private var timer: Timer? = null fun startTimer() { timer = Timer().apply { scheduleAtFixedRate(object : TimerTask() { override fun run() { // Background thread execution performBackgroundWork() } }, 0, 10_000) // Once in 10 seconds } } fun stopTimer() { timer?.cancel() timer = null }
4.3 Pros and cons
advantage: Easy to execute across threads, suitable for simple background timing;
shortcoming:
TimerTask
An exception will cause subsequent tasks to be unable to be executed; life cycles need to be managed manually;
5. Plan 3: ScheduledThreadPoolExecutor
5.1 Principle
based on, create a fixed-size thread pool and schedule single or periodic tasks.
5.2 Sample Code
private val scheduler = (1) fun startScheduledTask() { ({ performBackgroundWork() }, 0, 15, ) // Perform every 15 minutes} fun stopScheduledTask() { () }
5.3 Pros and cons
advantage: Controllable thread pool size, task exceptions will not affect other tasks;
shortcoming: Also affected by the process life cycle, it cannot be restarted across the process;
6. Plan 4: AlarmManager
6.1 Principle
System-level scheduling, usingAlarmManager
Can be triggered at a specified timePendingIntent
, wake up or start the component (BroadcastReceiver
、Service
、Activity
), supports cross-process and restart.
6.2 Sample Code
// Registered broadcast receiver: AlarmReceiverclass AlarmReceiver: BroadcastReceiver() { override fun onReceive(ctx: Context, intent: Intent) { // Execute tasks performWork(ctx) } } // exist<receiver android:name=".AlarmReceiver" /> // Set Alarm in the codeval am = getSystemService(Context.ALARM_SERVICE) as AlarmManager val pi = (this, 0, Intent(this, AlarmReceiver::), 0) // Accurate alarm clock (API 19+ may be merged)( AlarmManager.RTC_WAKEUP, () + 60_000, // 1 minute later pi )
Periodic tasks:
setRepeating()
Or inonReceive
Register again;Cross-restart recovery: Need to monitor
BOOT_COMPLETED
Re-register the alarm clock.
6.3 Pros and cons
advantage: System-level wake-up, which can ensure execution in reboot and Doze mode;
shortcoming: Frequent alarm clocks will consume severe power; API 19+ may be saved and merged by the system;
7. Plan 5: JobScheduler
7.1 Principle
Android 5.0+ native API manages background tasks that meet the criteria (network, charging, idleness, etc.). The system is scheduled according to the policy, and no need for developers to manually re-register.
7.2 Sample Code
class MyJobService: JobService() { override fun onStartJob(params: JobParameters): Boolean { // Execute in the background thread doWork { jobFinished(params, false) } return true // There is also background thread work } override fun onStopJob(params: JobParameters) = false } // Scheduled in Activity or Applicationval tm = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler val job = (1, ComponentName(this, MyJobService::)) .setRequiresCharging(false) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) .setPeriodic(15 * 60 * 1000) // Minimum 15 minutes .build() (job)
7.3 Pros and cons
advantage: The system automatically optimizes scheduling, saves power; supports conditional triggering;
shortcoming: API 21+, minimum cycle of 15 minutes;
8. Plan 6: WorkManager
8.1 Principle
Google's recommended background task library, compatible with API 14+, internally selected according to the system versionJobScheduler
/ AlarmManager
/ FirebaseJobDispatcher
, supports constraints, chains, unique tasks, delays, cycles, persistence, retry and other functions.
8.2 Sample Code
class MyWorker(ctx: Context, params: WorkerParameters): Worker(ctx, params) { override fun doWork(): Result { performWork(applicationContext) return () } } // Scheduling in codeval request = PeriodicWorkRequestBuilder<MyWorker>(1, ) .setConstraints(() .setRequiredNetworkType() .build()) .build() (this).enqueueUniquePeriodicWork( "my_hourly_work", , request )
8.3 Pros and cons
advantage: API compatibility, automatic selection of the best scheduler, persistence, and easy to use;
shortcoming: The scheduling does not guarantee accuracy and timeliness, and most scenarios are delayed by several minutes or longer;
9. Plan 7: Front Desk Service
9.1 Principle
Start aFront Desk Service(startForeground()
), useHandler
orScheduledExecutor
Execute tasks within it to ensure that processes and Service are not killed by the system.
9.2 Sample Code
class ForegroundTimerService: Service() { private val handler = Handler(()) private val task = object: Runnable { override fun run() { performWork(this@ForegroundTimerService) (this, 5*60*1000) } } override fun onCreate() { () startForeground(1, buildNotification()) (task) } override fun onDestroy() { (task) () } override fun onBind(intent: Intent?) = null }
9.3 Pros and cons
advantage: The process is permanent, not easy to be recycled, suitable for high-reliability long-term tasks;
shortcoming: Continuous display of notifications, consumes power, affects user experience;
10. Environment and dependence
// app/ plugins { id '' id 'kotlin-android' } android { compileSdk 34 defaultConfig { applicationId "" minSdk 21 targetSdk 34 } } dependencies { implementation ':work-runtime-ktx:2.8.1' }
11. Complete code integration
// ======================================================= // document:// Description: Declare Service and BroadcastReceiver// ======================================================= <manifest xmlns:andro package=""> <application android:name=".App"> <!-- AlarmManager Receiver --> <receiver android:name=".AlarmReceiver"/> <!-- Foreground Service --> <service android:name=".ForegroundTimerService" android:exported="false"/> <!-- JobScheduler Service --> <service android:name=".MyJobService" android:permission=".BIND_JOB_SERVICE"/> </application> </manifest> // ======================================================= // document:// Description: Application, initialize WorkManager// ======================================================= package import class App : Application() // ======================================================= // document:// Description: AlarmManager timed task reception// ======================================================= package import import import class AlarmReceiver : BroadcastReceiver() { override fun onReceive(ctx: Context, intent: Intent) { ("AlarmManager triggered") } } // ======================================================= // document:// Description: JobScheduler Service// ======================================================= package import import import .* class MyJobService: JobService() { private val scope = CoroutineScope() override fun onStartJob(params: JobParameters): Boolean { { ("JobScheduler triggered") jobFinished(params, false) } return true } override fun onStopJob(params: JobParameters) = false } // ======================================================= // document:// Description: Front Desk Service + Handler// ======================================================= package import import import import import .* class ForegroundTimerService: Service() { private val handler = Handler(()) private val task = object : Runnable { override fun run() { ("ForegroundService task executed") (this, 5*60*1000) } } override fun onCreate() { () startForeground(1, buildNotification()) (task) } override fun onDestroy() { (task) () } override fun onBind(intent: Intent?) = null private fun buildNotification(): Notification { val pi = ( this,0, Intent(this, MainActivity::),0) return (this, TaskUtils.CHANNEL_ID) .setContentTitle("Timed Task Service") .setSmallIcon(.ic_launcher_foreground) .setContentIntent(pi) .build() } } // ======================================================= // document:// Description: Tool class: Log and Scheduling Registration Method// ======================================================= package import import import import import import import import .* import object TaskUtils { const val CHANNEL_ID = "task_service_channel" fun scheduleAlarm(ctx: Context){ val am = (Context.ALARM_SERVICE) as AlarmManager val pi = ( ctx,0, Intent(ctx,AlarmReceiver::),0) ( AlarmManager.RTC_WAKEUP, ()+60_000, pi) } fun scheduleJob(ctx: Context){ val js = (Context.JOB_SCHEDULER_SERVICE) as JobScheduler val job = (1, ComponentName(ctx, MyJobService::)) .setPeriodic(15*60*1000) .build() (job) } fun scheduleWork(){ val req = PeriodicWorkRequestBuilder<MyWorker>( 1, ).build() () .enqueueUniquePeriodicWork( "my_hourly_work", , req) } } // ======================================================= // document:// Description: WorkManager Worker// ======================================================= package import import import class MyWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) { override fun doWork(): Result { ("WorkManager triggered") return () } } // ======================================================= // document:// Description: Demonstrate each solution trigger and start// ======================================================= package import import import .* import import class MainActivity : AppCompatActivity() { private lateinit var b: ActivityMainBinding override fun onCreate(s: Bundle?) { (s) b = (layoutInflater); setContentView() { (runnable, 5000) } { startTimer() } { startScheduled() } { (this) } { (this) } { () } { startService(Intent(this, ForegroundTimerService::)) } } // Handler example private val handler = Handler(()) private val runnable = object: Runnable { override fun run() { (" triggered") } } // Timer example private var timer: Timer? = null private fun startTimer(){ timer?.cancel() timer = Timer().apply { schedule(object: TimerTask(){ override fun run() { ("TimerTask triggered") } }, 0, 10000) } } // ScheduledThreadPoolExecutor Example private val scheduler = (1) private fun startScheduled(){ ({ ("ScheduledThreadPoolExecutor triggered") },0,30,) } }
12. Plan comparison and selection suggestions
plan | API version | Accuracy | Power consumption | Cross-restart | Applicable scenarios |
---|---|---|---|---|---|
() | API 1 | High (in thread) | high | ❌ | Short-term, lightweight cycle updates within the interface |
Timer / TimerTask | API 1 | Higher | Higher | ❌ | Simple background tasks |
ScheduledThreadPoolExecutor | API 1 | Higher | Higher | ❌ | Backend tasks that require thread pool management |
AlarmManager | API 1 | Can be accurate to milliseconds | Higher | ✅ | Accurate wake-up and restart at specified time |
JobScheduler | API 21+ | Batch scheduling, inaccurate | Low | ✅ | Conditional triggering, system optimization batch execution |
WorkManager | API 14+ | Approximate period (minutes) | Low | ✅ | Chainable, constrainable, recommended |
Foreground Service + Handler | API 1 | High (internal scheduling) | high | ❌ | Highly reliable, long-term background tasks, but affects UX |
forExtremely high accuracy requirementsandOne-timeReminder, use
AlarmManager
。forContinuous cycleBackground tasks that do not require second-level accuracy, recommended
WorkManager
orJobScheduler
。forRefresh within the UI for a short time,use
。
forProcess residentCore tasks that need to be continuously executed can be consideredFront Desk Service。
13. Performance and power saving optimization
Combined alarm: Avoid setting too many alarms and using batch or merge wake-up policies.
Avoid Doze restrictions: Use for non-critical missions
WorkManager
, let the system schedule uniformly;Dynamic adjustment cycle: Reduce the wake-up frequency according to the network, charging status, and user interaction;
Quickly complete short tasks:exist
JobService
Complete as soon as possible to avoid constant application;
14. Project summary and expansion ideas
This article comprehensively sorts out the seven main solutions for Android to implement timing tasks, from the simplestHandler
、Timer
, to the system levelAlarmManager
、JobScheduler
, and then to the best compatibilityWorkManager
And a high-reliability front desk service to help you according toApplication scenarios、AccuracyandPower consumptionSelection in three dimensions. At the same time, it provides complete and operational sample code, covering the entire process of registration, triggering, processing and cancellation, to help you quickly implement the timing task function.
Expand ideas
Mixed scheduling: Combining multiple solutions in the same scenario, for example, through
WorkManager
Manage long-term tasks and pass them at critical momentsAlarmManager
Accurate wake-up.Adaptive scheduling: Dynamically switch scheduling accuracy according to the App background/foreground status.
Visual management: Provides timed task list, run log and scheduling status monitoring within the application.
15. FAQ
-
Why is AlarmManager not accurate?
Android 19+ system will batch merge the alarm clock and can be used
setExactAndAllowWhileIdle()
Forced precision, but frequent wake-up will be restricted by Doze.
-
Why is the minimum cycle of JobScheduler 15 minutes?
The minimum system cycle is 15 minutes to avoid over-wake and power consumption.
-
Will WorkManager consume a lot of power?
No, the system will merge scheduling and execute only when the constraints are met, suitable for most background tasks.
-
Why does the front desk service affect the user experience?
Because notifications will be displayed continuously, and the resident process consumes power and it is difficult for users to turn off.
-
Is it necessary to register dynamically BOOT_COMPLETED?
If used
AlarmManager
The alarm clock needs to be re-registered after restarting;JobScheduler
andWorkManager
It will automatically recover.
The above is the detailed content of several ways to implement timing tasks (with source code). For more information about Android timing tasks, please pay attention to my other related articles!