Services
Services:
- Are long-running background processes which do not have any associated UI
- Continue to run even if the original application is closed
Examples:
- Downloading an app from an app store
- Playing music even when the music player is dismissed
- Maintaining a network connection in a chat app when they are using a different app
Basic:
- No UI components
- Must be declared in the app manifest
- May be private or public
Starting services:
- Manually using the
Context.startService()method- There will be a
onStartCommandcallback
- There will be a
- Another activity binds to a service via IPC using
bindService()- If public, the activity may belong to another app
- There will be
onBindandonUnbindcallbacks
Stopping services:
- By themselves when the task is completed, using
stopSelf - By the owning app via a call to
stopService - By the system if it needs to free some RAM
Responsiveness:
- Services run on the main thread of the hosting process
- By default, services do NOT create a separate execution thread
IntentServiceuses a worker thread to handle start requests
Service types:
- Foreground:
- Anything noticeable to the user (e.g. audio playback)
- The WorkManager API can be used to schedule referable, asynchronous tasks
- Background:
- Operations not directly noticed by the user
- The system imposes restrictions on background services
- Bound:
- When an application component binds itself to an existing service
- IPC used to interact with the service
- The service runs as long as one or more applications are bound to it
- It gets destroyed when no more applications are bound to it
Broadcast Receivers
A component that response to system-wide broadcast announcements.
Some broadcasts built-in to Android: screen turned off, battery low, storage low, picture captured, SMS received, SMS sent.
Permissions:
- Android 6.0 (Marshmallow, API level 23) introduced permission changes
- Normal permissions: shown to the user at install time
- Dangerous permissions:
- Must be declared at runtime
Receiving broadcasts:
- Subclass
BroadcastReceiverand implementonReceive - Create an
IntentFilterobject to specify the kinds of broadcasts you want - Register and unregister the receiver during
onResume()andonPause()
During development, broadcasts can be spoofed with adb e.g.
adb shell 'am broadcast -a android.intent.action.BOOT_COMPLETED'
Initiating broadcasts:
- Applications can initiate broadcasts to inform other applications of status or readiness
- TODO
Live Demo
Daily notification to take a picture.
<manifest ...>
<application ...>
<receiver
android:name=".AlarmReceiver"
android:enable="true"
android:exported="true" /* Broadcast scope is global, not within the application */
/>
</application>
</manifest>
fun Bundle.toParamsString() = keySet().map { "$it -> ${get(it)}" }.joinToString("\n")
// When the scheduled alarm event occurs, show a notification to the user
class AlarmReceiver: BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d(TAG, "${intent.action} with extras ${intent.extras.toBundleString()}")
val intent: PendingIndent = Intent(context, MainActivity::class.java).run {
// Open the main activity
PendingIntent.getActivity(
context,
requestCode = 0,
intent = this,
flags = 0
)
}
val notification = Notification.Builder(
context,
Notification.CATEGORY_REMINDER
).run {
setSmallIcon(R.drawable.camera)
setContentTitle("Notification title")
setContentText("Notification text")
// Run intent on click
setContentIntent(intent)
// Remove the notification on click
setAutoCancel(true)
build()
}
// May need to add `@SuppressLint("ServiceCast")` annotation to the method
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.notify(0, notification);
}
}
class MainActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
createNotificationChannel();
}
// Used so that users can disable notifications or set preferences on
// how they are delivered (e.g. deliver silently)
private fun createNotificationChannel() {
val channel = NotificationChannel(
Notification.CATEGORY_ALARM,
"Daily reminders",
NotificationManager.IMPORTANCE_DEFAULT
) {
description = "Send daily reminders"
}
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannel(channel)
}
}
// Also need to schedule the reminder on boot
object Utilities {
// Notifications fire immediately: use AlarmManager to schedule them
fun scheduleReminder(context: Context, hour: Int, minute: Int) {
val today = Calendar.getInstance().apply {
set(Calendar.HOUR_OF_DAY, hour)
set(Calendar.MINUTE, minute)
}
val intent = Intent(context, AlarmReceiver::class.java).let {
PendingIntent.getBroadcast(context, 0, it, FLAG_IMMUTABLE)
}
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.setInexactRepeating(
AlarmManager.RTC,
today.timeInMillis,
AlarmManager.INTERVAL_DAY,
intent)
}
}