Mobile Application DevelopmentUnit 915 min read
Notifications, Background Services & WorkManager in Android
Unit 9 of Mobile Application Development covers Android’s notification system (channels, types, and priority levels), background execution models (WorkManager, Foreground Services, and JobScheduler), and best practices for battery efficiency and user experience. It includes hands-on examples of scheduling tasks, handli
TAKEAWAYS:
- Android notifications use channels (Oreo+) to categorize alerts (e.g., messages, alarms) and require explicit user permission for critical notifications.
- WorkManager is the modern API for deferred and periodic background tasks, handling constraints like network availability or battery optimization.
- Foreground Services are visible to users (e.g., music players) and require a persistent notification to avoid being killed by the system.
- JobScheduler (deprecated in favor of WorkManager) was used for task scheduling with flexible constraints but lacked WorkManager’s reliability.
- Battery optimization tools (e.g., Android’s "Battery Saver") can restrict background tasks; developers must design for such constraints.
- Doze Mode and App Standby reduce background activity to save battery, requiring explicit wake locks or alarms for critical tasks.
Core Concepts: Notifications in Android
1. What Are Notifications?
Notifications are temporary messages displayed outside an app to inform users of events (e.g., new messages, updates). They appear in the status bar and can be expanded into a notification panel.
Key Components:
- Notification Channel (API 26+): Groups notifications by type (e.g., "Messages," "Reminders"). Users can customize channel settings (sound, vibration, priority).
- Notification Builder: Constructs notifications with icons, text, actions (e.g., "Reply," "Dismiss"), and priority levels (
PRIORITY_HIGH,PRIORITY_LOW). - Notification Manager: System service to post and manage notifications.
Example: Sending a Basic Notification
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle("New Message")
.setContentText("You have a new chat from John")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true); // Removes notification on tap
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
notificationManager.notify(1, builder.build());
Visual: Notification Channel Setup
flowchart TD
A["Notification Channel\n(Created via NotificationManager)"] --> B["Set Channel Name\n(e.g., 'Alerts')"]
B --> C["Set Channel Importance\n(PRIORITY_HIGH/LOW/DEFAULT)"]
C --> D["Set Channel Description\n(e.g., 'Critical alerts')"]
D --> E["Enable Channel\n(Required for notifications to appear)"]
E --> F["Notifications\nPosted to Channel"]2. Notification Channels (API 26+)
Before Android 8.0 (Oreo), notifications could be sent without channels. Now, all notifications must belong to a channel, and channels must be defined before use.
Steps to Create a Channel:
- Define a channel ID (e.g.,
"reminders"). - Set importance (
IMPORTANCE_HIGH,IMPORTANCE_LOW, etc.). - Enable the channel via
NotificationManager.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
"reminders",
"Reminders",
NotificationManager.IMPORTANCE_HIGH
);
channel.setDescription("Important reminders from the app");
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
Visual: Notification Priority Levels
3. Notification Actions and Styles
Notifications can include actions (buttons) and styles for richer content:
- BigTextStyle: Expands text into a large view.
- BigPictureStyle: Displays an image.
- MessagingStyle: Shows a conversation thread.
- InboxStyle: Lists multiple items (e.g., emails).
Example: BigTextStyle Notification
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle("Long Message")
.setContentText("Preview...")
.setStyle(new NotificationCompat.BigTextStyle()
.bigText("This is a very long message that will be expanded when the user pulls down the notification panel. It can include multiple lines of text to provide detailed information without taking up too much space in the compact view."))
.setSmallIcon(R.drawable.ic_notification);
Visual: Notification Expansion
Background Services and Task Scheduling
1. Why Background Services?
Mobile apps often need to perform tasks without a user actively interacting with them:
- Syncing data (e.g., WhatsApp messages).
- Processing uploads/downloads (e.g., Pathao ride updates).
- Running periodic maintenance (e.g., Daraz inventory checks).
2. Background Execution Models
Android provides multiple ways to run tasks in the background, each with trade-offs:
| Model | Use Case | Battery Impact | User Visibility | API Stability |
|---|---|---|---|---|
| WorkManager | Deferred/periodic tasks | Low | No | Recommended |
| Foreground Service | Long-running tasks (e.g., music) | High | Yes (notification) | Required for long tasks |
| JobScheduler | Task scheduling with constraints | Medium | No | Deprecated |
| AlarmManager | Precise time-based tasks | High | No | Limited use |
Visual: Background Execution Models
classDiagram
class WorkManager {
+ schedule tasks with constraints
+ handles Doze Mode
+ replaces JobScheduler
}
class ForegroundService {
+ requires notification
+ runs indefinitely
+ used for long tasks
}
class JobScheduler {
+ deprecated
+ scheduled with network/battery constraints
}
class AlarmManager {
+ set exact alarms
+ battery-intensive
}
WorkManager --> ForegroundService : "Use for long tasks"
ForegroundService --> WorkManager : "Alternative for constraints"3. WorkManager: The Modern Approach
WorkManager is the recommended API for background tasks. It:
- Handles constraints (e.g., network availability, battery not low).
- Survives app restarts and device reboots.
- Works around Doze Mode and App Standby.
Key Classes:
WorkRequest: Defines a task (one-time or periodic).WorkManager: Enqueues and manages work.
Example: One-Time Work Request
WorkRequest uploadWorkRequest = new OneTimeWorkRequest.Builder(MyWorker.class)
.setConstraints(new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build())
.build();
WorkManager.getInstance(context).enqueue(uploadWorkRequest);
Worker Class (MyWorker.java):
public class MyWorker extends Worker {
public MyWorker(@NonNull Context context, @NonNull WorkerParameters params) {
super(context, params);
}
@NonNull
@Override
public Result doWork() {
// Perform background task (e.g., upload data)
uploadDataToServer();
return Result.success();
}
}
Visual: WorkManager Execution Flow
flowchart TD
A["WorkRequest\n(Enqueued)"] --> B["WorkManager\n(Checks constraints)"]
B -->|"Constraints met"| C["Worker\n(doWork() executed)"]
B -->|"Constraints not met"| D["Retry or cancel"]
C --> E["Result\n(success/failure)"]Trace: WorkManager Execution
| Step | Action | Constraints Check | Result |
|---|---|---|---|
| 1 | enqueue(uploadWorkRequest) |
Network required | Queued |
| 2 | Device connects to Wi-Fi | Network available | Worker starts |
| 3 | doWork() executed |
- | Uploads data |
| 4 | Result.success() returned |
- | Task completed |
4. Foreground Services: For Visible Background Tasks
Some tasks (e.g., music playback, file downloads) must run in the foreground to avoid being killed by the system. These require:
- A persistent notification in the status bar.
- The
START_FOREGROUND_SERVICEflag in the intent.
Example: Starting a Foreground Service
Intent serviceIntent = new Intent(context, MyForegroundService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(serviceIntent);
} else {
context.startService(serviceIntent);
}
Service Class (MyForegroundService.java):
public class MyForegroundService extends Service {
private NotificationChannel channel;
private Notification notification;
@Override
public void onCreate() {
createNotificationChannel();
createNotification();
startForeground(1, notification);
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
channel = new NotificationChannel(
"service_channel",
"Foreground Service",
NotificationManager.IMPORTANCE_LOW
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
}
private void createNotification() {
notification = new NotificationCompat.Builder(this, "service_channel")
.setContentTitle("Download in progress")
.setContentText("Downloading file...")
.setSmallIcon(R.drawable.ic_download)
.build();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Perform long-running task (e.g., download)
downloadFile();
return START_STICKY;
}
}
Visual: Foreground Service Lifecycle
sequenceDiagram
participant App as App
participant Service as ForegroundService
participant System as Android System
App->>Service: startForegroundService()
Service->>System: createNotificationChannel()
System-->>Service: Channel created
Service->>System: startForeground(1, notification)
System-->>Service: Service now foreground
Service->>Service: Perform long task
Service-->>App: Task completed (optional)Real-World Applications
1. eSewa: Transaction Notifications
- Idea Used: Notification Channels and WorkManager.
- How:
- eSewa sends transaction confirmation notifications via a high-priority channel (
"payments"). - Background tasks (e.g., retrying failed payments) use
WorkManagerwith constraints for network availability. - Visual:
flowchart TD A["User initiates\npayment"] --> B["eSewa app\nposts notification"] B --> C["Notification Channel:\n'Payments' (PRIORITY_HIGH)"] C --> D["User sees\nconfirmation"] D --> E["WorkManager\nretries failed transaction"]
- eSewa sends transaction confirmation notifications via a high-priority channel (
2. Pathao: Ride Updates via Foreground Service
- Idea Used: Foreground Service for real-time tracking.
- How:
- Pathao’s driver app uses a
ForegroundServiceto continuously update the rider’s location. - A persistent notification shows "Ride in progress" to avoid being killed.
- Visual:
flowchart LR A["Rider requests\nride"] --> B["Pathao starts\nForegroundService"] B --> C["Service updates\nlocation every 10s"] C --> D["Notification:\n'Ride ETA: 5 min'"] D --> E["Service stops\non ride completion"]
- Pathao’s driver app uses a
3. Ncell: Battery-Optimized Background Sync
- Idea Used: WorkManager with constraints.
- How:
- Ncell’s app syncs call logs and messages in the background.
- Uses
WorkManagerwith constraints:setRequiredNetworkType(NetworkType.CONNECTED)setRequiresBatteryNotLow(true)
- Visual:
flowchart TD A["User opens app"] --> B["WorkManager\nchecks constraints"] B -->|"Wi-Fi available"| C["Sync call logs"] B -->|"Battery low"| D["Delay sync"]
Common Pitfalls and Best Practices
1. Avoiding Battery Drain
- Use WorkManager instead of raw
AlarmManagerorService. - Minimize wake locks: Only use
WakeLockfor critical sections. - Leverage Doze Mode: Design tasks to work within Android’s battery-saving modes.
2. Handling User Restrictions
- Request ignore battery optimizations (for critical apps like banking):
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { Intent intent = new Intent(); String packageName = context.getPackageName(); PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); if (!pm.isIgnoringBatteryOptimizations(packageName)) { intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); intent.setData(Uri.parse("package:" + packageName)); context.startActivity(intent); } }
3. Notification Best Practices
- Group related notifications (e.g., chat messages from the same contact).
- Use
setAutoCancel(true)for notifications that should disappear on tap. - Avoid spamming: Limit high-priority notifications to critical events.
Exam Tip
What to Expect in TU/PU Exams:
Short Questions (2-5 marks):
- Define notification channels, WorkManager, or Foreground Service.
- Difference between
WorkManagerandJobScheduler. - When to use
setAutoCancel(true)in notifications.
Programming Questions (10-15 marks):
- Write code to:
- Create a notification channel.
- Schedule a
WorkRequestwith constraints. - Start a
ForegroundServicewith a notification.
- Trace execution: Given a
WorkManagersetup, predict when a task runs (e.g., "If the device is in Doze Mode, will the task execute?").
- Write code to:
Scenario-Based (5-10 marks):
- Design a background task for:
- A banking app (sync transactions hourly).
- A food delivery app (update rider location in real-time).
- Explain how you’d handle battery optimization or Doze Mode.
- Design a background task for:
Diagrams (3-5 marks):
- Draw the lifecycle of a
ForegroundService. - Show the flow of a
WorkRequestfrom enqueue to execution.
- Draw the lifecycle of a
Key Formulas/Concepts to Memorize:
- Notification Priority Levels:
PRIORITY_HIGH,PRIORITY_DEFAULT,PRIORITY_LOW. - WorkManager Constraints:
Constraints.Builder()methods (setRequiredNetworkType(),setRequiresBatteryNotLow()). - Foreground Service Requirement: Must call
startForeground()within 5 seconds of start.
Sample Exam Question and Answer:
Question: "Explain how you would implement a background task in an Android app that syncs user data every 6 hours, even if the device is in Doze Mode. Include code snippets and constraints."
Answer:
- Use WorkManager for reliability and Doze Mode compatibility.
- Set constraints to ensure the task runs only when:
- The device is charged or on AC power (to save battery).
- Wi-Fi is available (to avoid mobile data costs).
- Schedule a periodic task with a fixed delay of 6 hours.
// Schedule a periodic sync task
PeriodicWorkRequest syncWork = new PeriodicWorkRequest.Builder(
SyncWorker.class,
6, TimeUnit.HOURS
).setConstraints(new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresCharging(true) // Optional: run only when charged
.build())
.build();
WorkManager.getInstance(context).enqueue(syncWork);
Visual: WorkManager Constraints Flow
flowchart TD
A["PeriodicWorkRequest\n(6-hour interval)"] --> B["Constraints:\nWi-Fi + Charging"]
B -->|"Conditions met"| C["Worker executes"]
B -->|"Conditions not met"| D["Task delayed"]Trace Table:
| Time | Device State | Constraints Met? | Action |
|---|---|---|---|
| 10:00 AM | Wi-Fi, Charging | Yes | SyncWorker runs |
| 4:00 PM | Mobile Data, 20% | No | Task delayed |
| 10:00 PM | Wi-Fi, Charging | Yes | SyncWorker runs |
Final Tip: Always test your background tasks on real devices (emulators may not fully simulate Doze Mode). Use Android’s Battery Historian to analyze battery impact.
Based on the TU BIM syllabus for Mobile Application Development (IT272), unit 9.
Discussion
Loading…