Background Work in Android Studio
Overview
You should not do blocking tasks on UI Thread. Such as
- decoding bitmap
- accessing storage
- working on machine learning model
- network request
Definition of background work
An app is running in the background when both the following conditions are satisfied:
- No activities of an application are currently visible to the user.
- When an application was visible to the user then that application didn't start any foreground services.
Type of background work
Approaches to background work
- Below figure can be used to guide which approach is used for a particular type of work/task.
Immediate Work
- Immediate work encompasses tasks which need to execute right away.
- For persistent immediate work, you should use WorkManager with a OneTimeWorkRequest. Expedite a WorkRequest with setExpedited().
- For impersistent immediate work, you should you use Kotlin coroutines. If your app uses the Java programming language, you should use RxJava or Guava. You can also use Executors.
Long-Running Work
- Work is long running if it is likely to take more than ten minutes to complete.
- WorkManager allows you to handle such tasks using a long-running Worker.
Deferrable Work
- Deferrable work is any work that does not need to run right away.
- Scheduling deferred work through WorkManager is the best way to handle tasks that don't need to run immediately but which ought to remain scheduled when the app closes or the device restarts.
Alarms
- Alarms are a special use case that are not a part of background work.
- You should only use AlarmManager only for scheduling exact alarms such as alarm clocks or calendar events.
Replacing Foreground Services
- Android 12 restricts launching foreground services from the background. For most cases, you should use setForeground() from WorkManager rather than handle foreground services yourself.
- Some use cases of Foreground Services
- Media Playback
- Activity Tracking
- Location Sharing
- Voice or Video Calls
Running Android Tasks in Background
- All Android apps use a main thread to handle UI operations.
- Calling long-running operations from this main thread can make your application freeze and unresponsive.
- You can create additional background threads to handle long-running operations and leave the main thread to handle UI updates
Creating Multiple Threads
- A thread pool is a managed collection of threads that run your tasks in parallel from a queue. New tasks are executed on some existing threads that becomes idle.
- To send a task to a thread pool, use the ExecutorService interface. ExecutorService is not a Service
- Creating threads is expensive, so you should create a thread pool only once as your app initializes.
class MyApplication : Application() {
private var executorService: ExecutorService? = null
override fun onCreate() {
super.onCreate()
executorService = Executors.newFixedThreadPool(4)
}
companion object {
const val TAG: String = "MyApplication"
}
}
- Be sure to save the instance of the ExecutorService either in your Application class or in a dependency injection container.
Executing in a Background Thread
- Making a network request on the main thread causes the thread to wait, or block, until it receives a response.
- Since the thread is blocked, the OS can't call onDraw(), and your app freezes, potentially leading to an Application Not Responding (ANR) dialog.
- The solution to this problem is to use thread pool.
- Any thread in your app can run in parallel to other threads, including the main thread, so you should ensure that your code is thread-safe. For this purpose, you should avoid sharing mutable state between threads whenever possible.
Communicating with the main thread
- We can use callbacks to communicate with the main thread. Make sure to add such callback methods in the execute() of Executor in ViewModel.
- Note: To communicate with the View from the ViewModel layer, use LiveData as recommended in the Guide to app architecture. If the code is being executed on a background thread, you can call MutableLiveData.postValue() to communicate with the UI layer.
Using Handlers
- Handler adds the action which is to be performed on different threads, in a queue. Now to specify which thread is to be used to run the action we can use Looper.
- A Looper is an object that runs the message loop for an associated thread.
- To retrieve the main thread we can use the getMainLooper() method of Looper object.
- Be sure to save the instance of the Handler either in your Application class or in a dependency injection container (where you have created the Executor earlier).
- For best practices, we should inject the Handler into the repository and for more flexibility we can pass the Handler into each function too. In this way, we can directly modify the UI from the callback or use LiveData.setValue() to communicate with the UI.
Configuring a Thread Pool
- You can create a thread pool in two ways
- Using Executor
- Using ThreadPoolExecutor
- Below is the example of creating thread pool in both the ways
class MyApplication : Application() {
private val NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors()
private val workQueue: BlockingDeque<Runnable> = LinkedBlockingDeque<Runnable>()
private val KEEP_ALIVE_TIME = 1L
private val KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS
private val threadPoolExecutor: ThreadPoolExecutor = ThreadPoolExecutor(
NUMBER_OF_CORES, NUMBER_OF_CORES, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, workQueue
)
private var executorService: ExecutorService? = null
private val mainThreadHandler: Handler = HandlerCompat.createAsync(Looper.getMainLooper())
override fun onCreate() {
super.onCreate()
executorService = Executors.newFixedThreadPool(4)
threadPoolExecutor.execute {
Log.i(TAG,"Running a task in ThreadPool")
}
}
companion object {
const val TAG: String = "MyApplication"
}
}
Concurrency Libraries
- For Java you can use RxJava or Guava libraries.
- For Kotlin you can use Coroutines.
Background Optimizations
- Background processes can be memory- and battery-intensive.
- An implicit broadcast may start many background processes that have registered to listen for it. This can have a substantial impact on both device performance and user experience.
User-Initiated Restrictions
- Beginning in Android 9 (API level 28), if an app exhibits some of the bad behaviors described in Android vitals, the system prompts the user to restrict that app's access to system resources.
- If the system notices that an app is consuming excessive resources, it notifies the user, and gives the user the option of restricting the app's actions.
Schedule network jobs on unmetered conditions.
- Below is the program to use unmetered network.
private val MY_BACKGROUND_JOB = 0;
fun scheduleJob(context: Context) {
val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
val job = JobInfo.Builder(MY_BACKGROUND_JOB, ComponentName(context, MyJobService::class.java))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setRequiresCharging(true)
.build()
jobScheduler.schedule(job)
}
- When the conditions for your job are met, your app receives a callback to run the onStartJob() method in the specified JobService.class.
class MyJobService: JobService() {
override fun onStartJob(parameters: JobParameters?): Boolean {
return true;
}
override fun onStopJob(parameters: JobParameters?): Boolean {
return true;
}
}
- To see more examples of JobScheduler implementation, see the JobScheduler sample app.
- You can also use WorkManager.
Monitor network connectivity while the application is running.
- Apps that are running can still listen for CONNECTIVITY_CHANGE with a registered BroadcastReceiver.
- Use ConnectivityManager API to monitor such networks
- Call registerNetworkCallback() method and then passes the NetworkRequest object to the system.
- When the network conditions are met, the app receives a callback to execute the onAvailable() method defined in its ConnectivityManager.NetworkCallback class.
Restrictions on receiving image and video broadcasts
- In Android 7.0 (API level 24), apps are not able to send or receive ACTION_NEW_PICTURE or ACTION_NEW_VIDEO broadcasts.
- You can use JobInfo and JobParameters to support such features.
private val MY_BACKGROUND_JOB = 0;
fun scheduleJob(context: Context) {
val jobScheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
val job =
JobInfo.Builder(MY_BACKGROUND_JOB, ComponentName(context, MyJobService::class.java))
.addTriggerContentUri(
JobInfo.TriggerContentUri(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS
)
)
.build()
jobScheduler.schedule(job)
}
- JobParameters API allow your application to receive useful information about what content authorities and URIs triggered the job, to learn more click here.
class MyJobService : JobService() {
override fun onStartJob(parameters: JobParameters): Boolean {
StringBuilder().apply {
append("Media Content has changed: \n")
parameters.triggeredContentAuthorities?.also { authorities ->
append("Authorities : ${authorities.joinToString(", ")} \n")
append(parameters?.triggeredContentUris?.joinToString("\n"))
} ?: append("No Content")
Log.i(MyApplication.TAG, toString())
}
return true
}
override fun onStopJob(p0: JobParameters?): Boolean {
return true
}
}
Further Optimize your application
- Optimizing your apps to run on low-memory devices, or in low-memory conditions, can improve performance and user experience.
- To simulate conditions where implicit broadcasts and background services are unavailable
$ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND ignore
- To re-enable implicit broadcasts and background services
$ adb shell cmd appops set <package_name> RUN_IN_BACKGROUND allow
Broadcasts Overview
- Android apps can send or receive broadcast messages from the Android system and other Android apps.
- Apps can register to receive specific broadcasts. When a broadcast is sent, the system automatically routes broadcasts to apps that have subscribed to receive that particular type of broadcast.
System Broadcasts
- The system automatically sends broadcasts when various system events occur. These broadcasts are sent to all apps that are subscribed to receive the event.
- The broadcast message itself is wrapped in an Intent object whose action string identifies the event that occurred.
- For a complete list of system broadcast actions, see the BROADCAST_ACTIONS.TXT file in the Android SDK.
Changes to System Broadcasts
- Beginning with Android 9 (API level 28), The NETWORK_STATE_CHANGED_ACTION broadcast doesn't receive information about the user's location or personally identifiable data.
- If your app targets Android 8.0 or higher, you cannot use the manifest to declare a receiver for most implicit broadcasts. You can still use a context-registered receiver when the user is actively using your app.
Receiving Broadcasts
- There are two ways to receive broadcasts
- Manifest-declared Receivers
- Declare the broadcast in AndroidManifest.xml file.
<receiver
android:name=".broadcasts.MyBroadcast"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.AIRPLANE_MODE" />
</intent-filter>
</receiver>
- Attribute andoird:name and android:exported is mandatory
- The intent filters specify the broadcast actions your receiver subscribes to.
- Create a subclass of BroadCastReceiver and overrice onReceive(Context, Intent)
class MyBroadcast : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
Toast.makeText(context, "Airplane Mode Changed", Toast.LENGTH_LONG).show()
}
}
}
- The system package manager registers the receiver when the app is installed.
- The receiver then becomes a separate entry point into your app which means if your application is not running, the system can start the app and deliver the broadcast through that entry point.
- The system creates a new BroadcastReceiver component object to handle each broadcast that it receives. This object is valid only for the duration of the call to onReceive(Context, Intent). Once your code returns from this method, the system considers the component no longer active (this means onRecieve() runs on main thread in foreground).
- Context-registered Receivers
- Create an instance of BroadcastReceiver in the application component (Activity or Fragment)
val br: BroadcastReceiver = MyBroadcast()
- Create an IntentFilter and register the receiver by calling registerReceiver(BroadcastReceiver, IntentFilter)
val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION).apply {
addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED)
}
registerReceiver(br, filter)
- If you register within an Activity context, you receive broadcasts as long as the activity is not destroyed.
- If you register with the Application context, you receive broadcasts as long as the app is running.
- To stop receiving broadcasts, call unregisterReceiver(android.content.BroadcastReceiver). Be sure to unregister the receiver when you no longer need it or the context is no longer valid.
- If you register a receiver in onCreate(Bundle) using the activity's context, you should unregister it in onDestroy() to prevent leaking the receiver out of the activity context.
- If you register a receiver in onResume(), you should unregister it in onPause() to prevent registering it multiple times.
Effects on process state
- When a process executes a receiver (that is, currently running the code in its onReceive() method), it is considered to be a foreground process
- Once your code returns from onReceive(), the BroadcastReceiver is no longer active.
- Manifest-declared receiver is considered to be low-priority by the system and can be killed if required.
- You should not start long running background threads from a broadcast receiver. After onReceive(), the system can kill the process at any time to reclaim memory, and in doing so, it terminates the spawned thread running in the process.
- To avoid this you should use either goAsync() or schedule a JobService from the receiver using the JobScheduler.
Sending Broadcasts
- Android provides three ways to send broadcasts from an application component (Activity / Fragment)
- sendOrderedBroadcast (Intent, String)
- This method sends broadcasts to one receiver at a time.
- Each receiver executes in turn.
- The data can propagate from one receiver to another receiver.
- A receiver can abort other receivers.
- The order receivers run in can be controlled with the android:priority attribute of the matching intent-filter; receivers with the same priority will be run in an arbitrary order.
<intent-filter android:priority="@integer/material_motion_duration_short_1">
<action android:name="android.intent.action.AIRPLANE_MODE" />
</intent-filter>
- sendBroadcast (Intent)
- This method sends broadcasts to all receivers in an undefined order.
- Receivers cannot receive data from another receivers.
- The data cannot propagate from one receiver to another receiver.
- A receiver cannot abort other receivers.
Intent().also { intent ->
intent.setAction("com.example.myapplication.MY_NOTIFICATION")
intent.putExtra("data", "Sent from MY_NOTIFICATION")
sendBroadcast(intent)
}
- The intent's action string must provide the app's Java package name (e.g. "com.example.myapplication") syntax and uniquely identify the broadcast event.
- LocalBroadcastManager.sendBroadcast
- This method can send broadcasts to receivers that are in the same app as the sender.
- If you don't need to send broadcasts across apps, use local broadcasts.
- You don't need to worry about any security issues related to other apps being able to receive or send your broadcasts.
Requesting Broadcast with Permissions
- You can enforce restrictions on either the sender or receiver of a broadcast with the help of permissions.
- When you call sendBroadcast() or sendOrderedBroadcast(), you can specify a permission parameter.
- Only receivers who have requested that permission with the tag in their manifest (and subsequently been granted the permission if it is dangerous) can receive the broadcast.
sendBroadcast(intent, android.Manifest.permission.SEND_SMS)
- To receive the broadcast, the receiving app must request the permission as shown below
<uses-permission android:name="android.permission.SEND_SMS"/>
- You can specify either an existing system permission like SEND_SMS or define a custom permission with the <permission> element.
Receiving with Permissions
- If you specify a permission parameter when registering a broadcast receiver (either with registerReceiver(BroadcastReceiver, IntentFilter, String, Scheduler) or in <receiver> tag in your manifest), then only the broadcasters who have requested the permission with the <uses-permission> tag in their manifest can send an Intent to the receiver.
- Permission in manifest-declared receiver.
<receiver
android:name=".broadcasts.MyBroadcast"
android:exported="false"
android:permission="android.permission.SEND_SMS">
<intent-filter android:priority="@integer/material_motion_duration_short_1">
<action android:name="android.intent.action.AIRPLANE_MODE" />
</intent-filter>
</receiver>
- Permission in context-registered receiver.
registerReceiver(br, filter, android.Manifest.permission.SEND_SMS,null)
- In order to send the broadcasts to the above receivers, the sending app must request the permission as shown below
<uses-permission android:name="android.permission.SEND_SMS" />
Implicit Broadcast Exceptions
- As part of the Android 8.0 (API level 26) Background Execution Limits, apps that target the API level 26 or higher can no longer register broadcast receivers for implicit broadcasts in their manifest.
- To learn more please click here.
Manage Device Awake State
- When an Android device is left idle, it will first dim, then turn off the screen, and ultimately turn off the CPU. This prevents the device's battery from quickly getting drained
- Keep the Device Awake
- Alternatives to using wake locks
- If your app is performing long-running HTTP downloads, consider using DownloadManager.
- If your app is synchronizing data from an external server, consider creating a sync adapter.
- If your app relies on background services, consider using JobScheduler or Firebase Cloud Messaging to trigger these services at specific intervals.
- If you need to keep your companion app running whenever a companion device is within range, use Companion Device Manager.
- Keep the screen on
- The best way to do this is to use the FLAG_KEEP_SCREEN_ON in your activity (and only in an activity, never in a service or other app component)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d("MainActivity", "MyApplication :: MainActivity :: onCreate")
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}
- This approach doesn't require special permission.
- You can also use android:keepScreenOn attribute in the xml file.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
tools:context=".MainActivity">
</androidx.constraintlayout.widget.ConstraintLayout>
- Though it doesn't require but if you want to clear the flag then you can use.
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
- Keep the CPU on
- If you need to keep the CPU running in order to complete some work before the device goes to sleep, you can use Wake Locks (a feature of PowerManager).
- You should use wake locks only when it is strictly necessary and use it as short as possible. You should never use a wake lock in an activity. Wake Locks will impact your battery life.
- To use a wake lock we must first declare WAKE_LOCK in AndroidManifest.xml file
<uses-permission android:name="android.permission.WAKE_LOCK"/>
- Using a wake lock in broadcast receiver
class MyBroadcast : BroadcastReceiver() {
var mContext: Context? = null
val wakeLock: PowerManager.WakeLock =
(mContext?.getSystemService(Context.POWER_SERVICE) as PowerManager).run {
newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyApp :: MyWakeLockTag").apply {
acquire(2 * 60 * 1000L /*2 minutes*/)
}
}
override fun onReceive(context: Context?, intent: Intent?) {
mContext = context
if (intent?.action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
Toast.makeText(context, "Airplane Mode Changed", Toast.LENGTH_LONG).show()
}
wakeLock.release()
}
}
- To release the wake lock, call wakelock.release()
- Use a broadcast receiver that keeps the device awake
- Using a broadcast receiver with a service lets you manage the life cycle of a background task.
- To create and manage PARTIAL_WAKE_LOCK we must use WakefulBroadcastReceiver.
- To use a WakefulBroadcastReceiver declare it in the manifest file.
<receiver android:name=".broadcasts.MyWakefulReceiver"></receiver>
- Then create an IntentService
class WakefulIntentService : IntentService("WakefulIntentService") {
private val notificationManager: NotificationManager? = null
internal val builder: NotificationCompat.Builder? = null
override fun onHandleIntent(intent: Intent?) {
val extras: Bundle = intent?.extras!!
MyWakefulReceiver.completeWakefulIntent(intent) // This line is not working
}
}
- Then create a subclass of WakefulBroadcastReceiver
class MyWakefulReceiver : WakefulBroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Intent(context, WakefulIntentService::class.java).also { service ->
startWakefulService(context, service)
}
}
}
- Schedule Repeating Alarms
- You could use an alarm (AlarmManager) to initiate a long-running operation, such as starting a service once a day to download a weather forecast.
- You can use alarm with BroadcastReceivers and Services to perform tough background operations.
- Set an inexact alarm
- The system delivers an inexact alarm at a time when it thinks it's most efficient for the device's battery.
- Deliver an alarm with specific time.
- If your application calls the following methods then the alarm never goes off before the supplied triggered time.
- Deliver a time during a time window.
- If your application calls setWindow() method then the alarm never goes off before the supplied triggered time.
- Deliver a repeating alarm at a roughly intervals.
- If your application calls setInexactRepeating() then the system invokes multiple alarms. First alarm goes off within a specified time period. And then the subsequent alarm goes off on average after a specified time window elapses.
- Set an exact alarm
- The system delivers an exact alarm at the precise moment in future.
- If your app targets Android 12 (API level 31) or higher, you must declare the "Alarms & reminders" special app access; otherwise, a SecurityException occurs.
- You can set exact alarm using one of the following methods
- System Resource Consumption
- It is highly recommended to use inexact alarm instead of exact alarm, unless your app sets an exact alarm for an acceptable reason.
- To perform longer work, or work that requires network access, use WorkManager or JobScheduler.
- To perform work while the device is in Doze, create an inexact alarm using setAndAllowWhileIdle(), and start a job from the alarm.
- Acceptable use cases for using exact alarm
- Your application is an alarm clock or a timer application.
- Your application is a calendar application that shows notification of upcoming events.
- Declare the exact alarm permissions
- Use SCHEDULE_EXACT_ALARM in the <uses-permission> tab AndroidManifest.xml file.
- Check that your application still has permission
- When the "Alarms & reminders" special app access is granted to your app, the system sends it the ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED broadcast. Your app should implement a broadcast receiver that does the following:
- Call canScheduleExactAlarms() to confirm that your app still has the special app access.
- Reschedule any exact alarms that your app needs, based on its current state.
- Ask users to grant the app access
- You can use the intent action ACTION_REQUEST_SCHEDULE_EXACT_ALARM to jump to the screen shown on the web page.
- Set a Repeating alarm
- Repeating alarms allow the system to notify your app repeatedly.
- A repeating alarm has the following characteristics
- A alarm type.
- A trigger time.
- The alarm's interval.
- A pending intent.
- To create an instance of PendingIntent intent pass the flag FLAG_NO_CREATE to PendingIntent.getService().
val alarmManager = context?.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val pendingIntent =
PendingIntent.getService(context, 12, intent!!, PendingIntent.FLAG_NO_CREATE)
if (pendingIntent != null && alarmManager != null) {
alarmManager.cancel(pendingIntent)
}
- Understand the trade-offs
- If you own the server where you sync data of the application regularly then you should use Google Cloud Messaging (GCM) in conjunction with sync adapter rather than AlarmManager. GCM offers significantly more flexibility.
- Use this reference of Running a Sync Adapter.
- Choose an alarm type
- There are two general clock types of alarms:
- Elapsed Real Time
- It uses time since system boot.
- This type of alarm fires every 30 seconds.
- Real Time Clock (RTC)
- It uses UTC (wall clock) time.
- This type of alarm are dependent on current locale.
- Both types have a "wakeup" version, which says to wake up the CPU of a device when the screen of the device is off.
- Use Cases
- If you don't use the wakeup version of your alarm type, then all the repeating alarms will fire when your device is next awake.
- If you simply need your alarm to fire at a particular interval (e.g. every half an hour) use one of the Elapsed Real Time alarm.
- If you need your alarm to fire at a particular time of day, then choose one of the clock-based Real Time Clock alarm.
- List of types
- Decide how precise your alarm needs to be
- When you use setInexactRepeating() method, Android synchronizes multiple inexact repeating alarms and fires them at the same time. This reduces the drain on the battery.
- When your application needs precision of time then use an exact alarm by calling setRepeating()
- With setInexactRepeating(), you can't specify a custom interval the way you can with setRepeating(). You have to use one of the interval constants that is available in AlarmManager such as INTERVAL_FIFTEEN_MINUTES, INTERVAL_DAY etc.
- Cancel an alarm
- Call cancel(PendingIntent) method of AlarmManager.
- Set an alarm when the device restarts
- When a device shuts down all alarms are canceled automatically by default.
- Follow the following steps to avoid this behavior.
- Set the RECEIVE_BOOT_COMPLETED permission in AndroidManifest.xml file. With the permission the application will receiver ACTION_BOOT_COMPLETED broadcast from the system.
- Implement a BroadcastReceiver
- Add the receiver in AndroidManifest.xml file with an intent filter that filters on the ACTION_BOOT_COMPLETED action. The boot receiver is set to android:enabled = "false"
- Invoke alarms while the devices is in Doze mode.
- Android 6.0 supports Doze mode, which helps to extend battery life of a device.
- Alarms do not fire when the device is in Doze mode.
- Any scheduled alarms are deferred until the device exits Doze mode.
- If you need to complete work even when the device is idle use
- Best Practices
- Refer the above link to learn more.
Using a ListenableFuture
- ListenableFuture is not a part of Android Framework and instead provided by Guava.
- ListenableFuture is the result of an asynchronous computation: a computation that may or may not have finished producing a result yet.
- ListenableFuture is used in Jetpack libraries such as CameraX or Health Services.
- Required Libraries
implementation "com.google.guava:guava:31.0.1-android"
// To use CallbackToFutureAdapter
implementation "androidx.concurrent:concurrent-futures:1.1.0"
// Kotlin
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.6.0"
- Getting the result of ListenableFuture.
- Creating a ListenableFuture
- Creating an immediate future
- If your API is not asynchronous, but you need to wrap the result of a completed operation into a ListenableFuture, you can create an ImmediateFuture.
- Using a coroutine
- Converting a callback
- Converting from RxJava Single
Do's and Don'ts
- You should not do blocking tasks on UI Thread.
- Apps cannot send or receive ACTION_NEW_PICTURE or ACTION_NEW_VIDEO broadcasts.
- Apps targeting Android 7.0 (API level 24) and higher do not receive CONNECTIVITY_ACTION broadcasts if they declare their broadcast receiver in the manifest. Apps will still receive CONNECTIVITY_ACTION broadcasts if they register their BroadcastReceiver with Context.registerReceiver() and that context is still valid.
- Do not unregister in onSaveInstanceState(Bundle), because this isn't called if the user moves back in the history stack.
- You should not start long running background threads from a broadcast receiver.
- If you don't need to send broadcasts across apps, use local broadcasts.
- The most important topic of BroadcastReceiver is the Security Considerations and Best Practices.
- You should use wake locks only when strictly necessary and hold them for as short a time as possible. Means, you should never need to use a wake lock in an activity.
- If a PendingIntent is created with the flag FLAG_ONE_SHOT, it cannot be canceled.
- We recommend to use Elapsed Real Time alarm if you can.
Comments
Post a Comment