Android Services
Android Services Interview with follow-up questions
1. What is the difference between a Service and an IntentService in Android?
Both Service and IntentService are Android components for background work, but IntentService is deprecated as of API 30. Understanding both — and what replaced them — is important for interviews.
Service
A Service runs on the main thread by default. It does not automatically spin up a background thread, so you must manage threading yourself (typically with coroutines). It is the base class for all service types and remains the current, non-deprecated option.
Three lifecycle callbacks:
onCreate()— called once when first createdonStartCommand()— called each timestartService()is invoked; return value controls restart behavior (START_STICKY,START_NOT_STICKY,START_REDELIVER_INTENT)onDestroy()— called when the service is stopped
IntentService (deprecated)
IntentService was a convenience subclass that created a worker thread, queued incoming intents serially, and called your onHandleIntent(intent) override on that thread. It automatically stopped itself when the queue was empty.
Problems that led to deprecation:
- Used a single background thread (
HandlerThread) which couldn't take advantage of coroutines or thread pools - Could not report progress or interact with the UI cleanly
- No integration with lifecycle-aware components
Replacements
- WorkManager +
CoroutineWorker: For deferrable background tasks that need guaranteed execution. This is the recommended replacement for mostIntentServiceuse cases. - Foreground Service with coroutines: For long-running tasks that need to continue while the app is in the foreground and show a persistent notification (e.g., music playback, navigation, ongoing uploads). Use
ServiceCompat.startForeground()and launch coroutines with aServiceScope.
Common interview follow-ups
- What is
START_STICKYvsSTART_NOT_STICKY?START_STICKYtells the system to recreate the service after it's killed but with a null intent.START_NOT_STICKYtells the system not to recreate it unless there are pending intents. - Can a Service run after the app is killed? A started service can survive app process death depending on the
onStartCommandreturn value and system pressure, but is not guaranteed. WorkManager provides true persistence. - When do you need a foreground service vs WorkManager? Use a foreground service when work must run immediately and continuously (media playback, call in progress, navigation). Use WorkManager for deferrable work that can run when conditions are met.
Follow-up 1
Can you explain how to implement an IntentService?
To implement an IntentService, you need to create a subclass of IntentService and override the onHandleIntent(Intent intent) method. This method is called on a worker thread and receives the Intent that was used to start the service. Inside this method, you can perform the background task that you want to execute. Once the work is done, the service automatically stops itself. You can also override other methods such as onCreate() and onDestroy() if you need to perform additional setup or cleanup tasks.
Follow-up 2
What are some use cases for using a Service over an IntentService?
There are several use cases where using a Service instead of an IntentService is more appropriate:
- When you need to perform long-running tasks that require continuous execution, such as playing music or monitoring sensor data.
- When you need to interact with the user interface from the background thread, as IntentService does not provide a direct way to update the UI.
- When you need to handle multiple simultaneous requests, as IntentService processes requests sequentially.
- When you need more control over the lifecycle of the service, as IntentService automatically stops itself when the work is done.
Follow-up 3
How does the lifecycle of a Service differ from that of an IntentService?
The lifecycle of a Service and an IntentService is similar, but there are some differences:
Service: The
onCreate()method is called when the service is first created, followed byonStartCommand(Intent intent, int flags, int startId)to handle each start request. The service can be stopped by callingstopService(Intent intent)orstopSelf(). TheonDestroy()method is called when the service is about to be destroyed.IntentService: The
onCreate()method is called when the service is first created, followed byonHandleIntent(Intent intent)to handle each start request. The service automatically stops itself when the work is done, and theonDestroy()method is called.
In both cases, the service can also be bound to other components using the bindService(Intent intent, ServiceConnection conn, int flags) method.
2. How do you communicate between a Service and an Activity in Android?
Communication between a Service and an Activity depends on whether the service is bound or started, and the modern approach leans on shared ViewModel state or bound service interfaces rather than older patterns.
Bound Service with Binder (same process)
The most direct approach for in-process communication is a bound service that returns a custom Binder:
class MusicService : Service() {
inner class LocalBinder : Binder() {
fun getService(): MusicService = this@MusicService
}
override fun onBind(intent: Intent) = LocalBinder()
}
The Activity binds with bindService(), receives the IBinder in ServiceConnection.onServiceConnected(), and casts it to LocalBinder to get a direct reference to the service. This is the pattern used for media playback controllers and similar.
SharedViewModel via a scoped ViewModel (modern pattern)
For communication within the same app process, a ViewModel scoped to the application or an activity can serve as a shared state holder between the Activity UI and a service. The service updates a StateFlow; the Activity collects it. This eliminates direct coupling between the Service and Activity.
Broadcasts (for loose coupling or cross-process)
Use LocalBroadcastManager — or better, a StateFlow/event bus — for in-process loose coupling. For cross-process communication, sendBroadcast() with exported receivers works but has security implications (any app can listen). Prefer LocalBroadcastManager's replacement: simply using a shared singleton or StateFlow in a repository.
Note: LocalBroadcastManager itself is deprecated in newer Jetpack versions. The recommendation is to use LiveData or Flow for in-process event propagation.
Messenger (cross-process IPC)
Messenger wraps a Handler and allows passing Message objects across process boundaries. It provides a simple queued IPC mechanism without the complexity of AIDL. Useful for simple cross-process communication where you control both sides.
AIDL (complex cross-process IPC)
Android Interface Definition Language is for exposing a rich service API across processes. It's complex to implement and should only be used when your service must be called from other apps and Messenger is insufficient.
Common interview follow-ups
- How would you pass data from a running service to a Fragment? Bind the service and expose a
StateFlowfrom the service that the Fragment collects vialifecycleScope.launch { repeatOnLifecycle(STARTED) { ... } }. - What's the difference between
startServiceandbindService?startServicecreates a started service with independent lifecycle;bindServicecreates a connection whose lifetime is tied to the bound components. They can be used simultaneously. - How do you prevent the service from being killed while doing work? For user-visible ongoing operations, start as a foreground service with
startForeground().
Follow-up 1
What is a Bound Service?
A Bound Service is a type of Service in Android that allows other components, such as Activities, to bind to it and interact with it. When a component binds to a Bound Service, it can call methods on the Service and receive data from it. The Bound Service runs in the same process as the component that binds to it, and it can be used to perform long-running operations or provide a remote interface to another process.
Follow-up 2
Can you explain the use of Messenger class for communication?
The Messenger class in Android is a way to perform inter-process communication (IPC) between a Service and an Activity. It provides a simple way to send and receive messages between the two components.
To use the Messenger class, you need to create a Messenger object in both the Service and the Activity. The Service uses the Messenger object to send messages to the Activity, and the Activity uses the Messenger object to send messages to the Service.
The Messenger class internally uses a Handler to handle incoming messages. The Service creates a Handler and passes it to the Messenger constructor, and the Activity implements a Handler and passes it to the Messenger constructor. When a message is received, the Handler's handleMessage() method is called, allowing the component to handle the message.
Follow-up 3
What role does the onBind() method play in this communication?
The onBind() method is a callback method that is called when a component binds to a Bound Service. It is responsible for returning an IBinder object that the component can use to communicate with the Service.
The onBind() method is typically implemented by the Bound Service and returns an instance of a class that extends the Binder class. The Binder class is a basic implementation of the IBinder interface, which is used for communication between processes in Android.
The IBinder object returned by the onBind() method can be used by the component to call methods on the Service and receive data from it. The component can also pass the IBinder object to other components, allowing them to bind to the Service and communicate with it as well.
3. What are Broadcast Receivers in Android?
Broadcast Receivers are Android components that listen for system-wide or app-specific broadcast messages and execute code in response. They enable loose coupling between app components and between apps and the system.
How they work
A broadcast is sent via sendBroadcast(intent), sendOrderedBroadcast(), or LocalBroadcastManager.sendBroadcast(). Registered receivers whose IntentFilter matches the intent's action are invoked. The onReceive() method runs on the main thread and must complete quickly (under 10 seconds, and ideally much faster) — if you need to do longer work, schedule it with WorkManager or start a foreground service from onReceive().
Two registration methods
- Manifest-declared (static): Registered in
AndroidManifest.xml. These can receive broadcasts even when the app is not running, but since Android 8.0 (API 26), most implicit broadcasts can no longer wake a stopped app this way. Only a specific whitelist of implicit broadcasts (e.g.,BOOT_COMPLETED,LOCKED_BOOT_COMPLETED) can still use static registration. - Context-registered (dynamic): Registered at runtime via
context.registerReceiver()and unregistered withunregisterReceiver(). These only receive broadcasts while the component is active. Since Android 13 (API 33), dynamically registered receivers for exported broadcasts require explicitly declaringRECEIVER_EXPORTEDorRECEIVER_NOT_EXPORTED.
Common system broadcasts
Intent.ACTION_BOOT_COMPLETED— device finished bootingIntent.ACTION_BATTERY_LOW/ACTION_BATTERY_OKAYConnectivityManager.CONNECTIVITY_ACTION(deprecated; useNetworkCallbackinstead)Intent.ACTION_AIRPLANE_MODE_CHANGED
Modern considerations
- Connectivity changes: Do not use
CONNECTIVITY_ACTIONbroadcast for network monitoring. Register aNetworkCallbackviaConnectivityManager.registerNetworkCallback()— it is more accurate and lifecycle-aware. - Security: Broadcasts sent without permissions can be received by any app. Use
sendBroadcast(intent, permission)to restrict receivers, or useLocalBroadcastManager(deprecated but functional) / Flow for in-process events. goAsync(): If you need slightly more time inonReceive(), callgoAsync()to get aPendingResulttoken that extends the broadcast window. Still, hand off real work to a coroutine or WorkManager quickly.
Common interview follow-ups
- What happens if you don't unregister a dynamic receiver? It leaks the context and continues receiving broadcasts, which can cause crashes if the associated Activity or Fragment has been destroyed.
- What are ordered broadcasts?
sendOrderedBroadcast()delivers the broadcast to receivers one at a time in priority order. Each receiver can modify or abort the broadcast before the next one receives it. - Why were implicit broadcasts restricted in Oreo? To reduce unnecessary app wake-ups that drain battery. Apps should use explicit intents or other mechanisms (WorkManager,
NetworkCallback) instead.
Follow-up 1
How do you register a Broadcast Receiver?
To register a Broadcast Receiver in Android, you need to define a class that extends the BroadcastReceiver class and override the onReceive() method. Then, you can register the receiver either statically in the AndroidManifest.xml file or dynamically in your code using the registerReceiver() method. When registering dynamically, you also need to specify an IntentFilter to specify the types of broadcasts your receiver should listen for.
Follow-up 2
What is the difference between a Local Broadcast and a Global Broadcast?
The main difference between a Local Broadcast and a Global Broadcast in Android is the scope of the broadcast.
Local Broadcasts are only sent within your app and are not visible to other apps. They are more efficient and secure as they don't expose sensitive information to other apps. Local Broadcasts are typically used for communication between components within your app.
Global Broadcasts, on the other hand, are sent to all apps on the device. They can be received by any app that has registered a Broadcast Receiver for the specific broadcast. Global Broadcasts are useful for system-wide events or for communication between different apps.
Follow-up 3
Can you explain the lifecycle of a Broadcast Receiver?
The lifecycle of a Broadcast Receiver in Android is as follows:
Registration: The receiver is registered either statically in the AndroidManifest.xml file or dynamically in code using the registerReceiver() method.
Receive: When a broadcast that matches the receiver's IntentFilter is sent, the onReceive() method of the receiver is called. This is where you handle the received broadcast.
Execution: The onReceive() method should execute quickly as it runs on the main thread. If you need to perform long-running tasks, it is recommended to start a separate background thread or use a foreground service.
Unregistration: If the receiver was registered dynamically, it should be unregistered using the unregisterReceiver() method when it is no longer needed. This helps to prevent memory leaks.
4. What is a JobScheduler in Android?
JobScheduler is a system API introduced in Android 5.0 (API 21) for scheduling deferrable background work with constraints. Rather than running a task immediately, you describe the conditions under which the job can run, hand it to the OS, and the system batches and executes it when those conditions are met — reducing battery drain by avoiding unnecessary wake-ups.
How it works
You subclass JobService and implement onStartJob() and onStopJob():
class SyncJobService : JobService() {
override fun onStartJob(params: JobParameters): Boolean {
// Return true if work continues asynchronously
// Call jobFinished(params, needsReschedule) when done
return true
}
override fun onStopJob(params: JobParameters): Boolean {
// Return true to reschedule if interrupted
return true
}
}
Schedule a job with JobScheduler.schedule(JobInfo):
val jobInfo = JobInfo.Builder(JOB_ID, ComponentName(context, SyncJobService::class.java))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setRequiresCharging(true)
.setPeriodic(AlarmManager.INTERVAL_HOUR)
.build()
(context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler).schedule(jobInfo)
Constraints you can express
- Network type (any, unmetered/Wi-Fi, not roaming)
- Battery not low
- Device charging
- Device idle
- Minimum latency and deadline
- Content URI changes (triggers job when specific content changes)
Use WorkManager in application code
For most application-level use cases, use WorkManager instead of JobScheduler directly. WorkManager is the Jetpack-recommended API that abstracts over JobScheduler (and older mechanisms), adds guaranteed execution across process death, CoroutineWorker support, work chaining, and observable status via Flow.
Know JobScheduler conceptually — interviewers expect you to understand what WorkManager is built on — but signal that WorkManager is the right choice for app-level deferrable work.
Common interview follow-ups
- What's the difference between
JobSchedulerandAlarmManager?AlarmManagerfires at a precise time and can wake the device from Doze.JobScheduleris deferrable and batched by the OS for efficiency. Android 12 (API 31) added exact alarm restrictions. - What is Doze mode and how does it affect jobs? In Doze mode, the system defers network access and
JobSchedulerjobs until maintenance windows. WorkManager handles Doze gracefully without special handling in app code. - What replaced
IntentServicefor triggered background work? WorkManager with aCoroutineWorkeris the recommended replacement.
Follow-up 1
How does JobScheduler improve the efficiency of background tasks?
JobScheduler improves the efficiency of background tasks in several ways:
Job batching: JobScheduler can batch multiple jobs together, which reduces the number of times the device needs to wake up from idle mode, resulting in better battery life.
Job constraints: JobScheduler allows you to set constraints on when a job can run, such as requiring the device to be connected to a Wi-Fi network or charging. This ensures that jobs are executed at the most appropriate time, based on the device's current state.
Job rescheduling: JobScheduler automatically reschedules failed or cancelled jobs, ensuring that they eventually get executed.
Job prioritization: JobScheduler allows you to prioritize jobs, so that more important or time-sensitive jobs are executed first.
Follow-up 2
Can you explain how to schedule a job using JobScheduler?
To schedule a job using JobScheduler, you need to follow these steps:
Create a JobService subclass: Extend the JobService class and implement the onStartJob() and onStopJob() methods to define the work to be done in the background.
Create a JobInfo object: Use the JobInfo.Builder class to create a JobInfo object, which specifies the details of the job, such as the JobService to be used, the job ID, and any constraints or conditions.
Schedule the job: Use the JobScheduler.schedule() method to schedule the job by passing in the JobInfo object.
Here's an example code snippet that demonstrates how to schedule a job using JobScheduler:
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo jobInfo = new JobInfo.Builder(JOB_ID, new ComponentName(this, MyJobService.class))
.setRequiresCharging(true)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.build();
jobScheduler.schedule(jobInfo);
Follow-up 3
What are the different types of network-related conditions that can be set for a job?
JobScheduler allows you to set network-related conditions for a job, which determine when the job can run based on the device's network state. The different types of network-related conditions that can be set are:
NETWORK_TYPE_NONE: The job can run regardless of the device's network state.
NETWORK_TYPE_ANY: The job can run as long as the device has a network connection, regardless of the type of network (e.g., Wi-Fi or mobile data).
NETWORK_TYPE_UNMETERED: The job can run only when the device is connected to an unmetered network, such as Wi-Fi. This is useful for tasks that require a large amount of data to be transferred.
NETWORK_TYPE_NOT_ROAMING: The job can run only when the device is not roaming, i.e., connected to its home network. This is useful for tasks that should not be performed while the device is in a different country or region.
These network-related conditions can be set using the setRequiredNetworkType() method of the JobInfo.Builder class.
5. What are the different types of Services in Android?
Android services fall into three main types based on how they are started and how long they run.
Started Services
A started service is launched by startService() or startForegroundService() and runs independently until it calls stopSelf() or another component calls stopService(). It runs even after the component that started it is destroyed. The onStartCommand() return value controls restart behavior:
START_STICKY: System recreates the service after killing it, passing a null intent. Useful for ongoing services that should stay running (e.g., a persistent connection).START_NOT_STICKY: System does not recreate the service after killing it unless there are pending intents. Good for work that can safely be dropped.START_REDELIVER_INTENT: System recreates the service and re-delivers the last intent. Useful when you need to guarantee the intent is processed (e.g., a download task).
Bound Services
A bound service is started by bindService() and runs as long as at least one component is bound to it. It exposes a client-server interface via IBinder. When all clients unbind, the service is destroyed. Bound services are used for in-process communication (e.g., a media playback service that the Activity controls) and for cross-process IPC via AIDL or Messenger.
Foreground Services
A foreground service is a started service that displays a persistent notification, indicating to the user that the app is performing ongoing work. It has a higher priority and is much less likely to be killed under memory pressure. Use cases: music playback, navigation, fitness tracking, file upload/download.
Since Android 9 (API 28), FOREGROUND_SERVICE permission is required. Android 14 (API 34) introduced foreground service types (mediaPlayback, location, camera, microphone, etc.) that must be declared in the manifest and specified when calling startForeground(). This allows the system to understand and enforce what the service actually needs.
ServiceCompat.startForeground(
this,
NOTIFICATION_ID,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
)
IntentService (deprecated)
IntentService was a fourth pattern — a started service that handled requests on a worker thread serially. It was deprecated in API 30. Replace it with WorkManager + CoroutineWorker for deferrable work, or a foreground service with coroutines for user-visible ongoing work.
Common interview follow-ups
- Can a service be both started and bound? Yes. A service can be started with
startService()and also have components bound to it viabindService(). It stays alive until it is explicitly stopped and all bindings are released. - What happens when the system kills a foreground service? It is unlikely under normal circumstances. If it does happen (during critical system pressure),
START_STICKYwill cause it to restart. - What are foreground service types and why do they matter? Introduced in Android 10 and enforced more strictly in Android 14, they declare what sensitive resources (camera, microphone, location) the service needs, allowing targeted permission enforcement.
Follow-up 1
What is a Foreground Service?
A Foreground Service is a type of service that has a higher priority than normal background services. It is designed to perform tasks that are noticeable to the user and require ongoing interaction. Foreground services display a persistent notification in the notification bar, which provides ongoing information to the user and indicates that the service is running in the foreground.
Follow-up 2
How does a Foreground Service differ from a Background Service?
Foreground services and background services differ in terms of priority and visibility to the user.
Priority: Foreground services have a higher priority than background services. This means that the system is less likely to terminate a foreground service to free up resources.
Visibility: Foreground services display a persistent notification in the notification bar, which provides ongoing information to the user. This notification cannot be dismissed by the user unless the service is stopped or removed from the foreground.
In contrast, background services do not display a notification and are less visible to the user.
Follow-up 3
Can you explain the lifecycle of a Foreground Service?
The lifecycle of a Foreground Service is similar to that of a normal Service, with some additional considerations:
onCreate(): The service is created when
startForeground()is called. This method is used to perform one-time setup tasks.onStartCommand(): This method is called each time
startForeground()is called. It is used to handle the start command and perform the desired tasks.onDestroy(): The service is destroyed when
stopForeground()is called. This method is used to clean up any resources used by the service.
It is important to note that a Foreground Service must call startForeground() within 5 seconds of being started, otherwise the system will consider it unresponsive and throw an exception.
Live mock interview
Mock interview: Android Services
- Read your scene and goals
- Talk it out; goals tick off live
- Get a score and stronger lines
Your voice and your AI key never touch our servers; the key stays in this browser and is sent only to Google. Only your round scores are saved to track progress.