Running android tasks in background thread

Android Background Tasks — Under the surface!

Jun 9, 2018 · 10 min read

1. Services
When we have background running components we need to be very conscious of how it’s consuming resources.

Some tasks don’t fit nicely into the lifecycle of a single activity (ex. Database re-sync with the server). In general — It’s not a good idea to make network transactions that talk to a database inside of an activity. It has nothing to directly do with the user interface of the application.

Services are of the 4 primary Android components as described in the Android manifest.

Services are meant for long running background tasks that don’t need a visual component.

S e rvices don’t provide user interface.

An activity can start a service, using intent, which will then continue to run even after the activity is shut down.

Services are great for things like loading and processing data in the background, even when our app activities are closed.

Service vs. Loader
When to use which?

Loaders — We use loaders mostly when the background task is loading information that will only be used in the activity.
(1) Tied to the activity lifecycle
(2) Easy to make UI changes and communicate with activity
Ex: Decoding an image that was going to be used in an image view, or populate a recyclerView from a database, or if we want to fetch data we need to the UI rather then cache it into the database.

Services — When the task we are doing is decoupled from the UI.
(1) Decoupled from the UI
(2) Exists even when there is no UI
Ex: Updating a database in the background.

In short, if you’re loading or processing data that will be used in the UI — use loader.
If you need to process, upload or download data in a way where the end result not directly affect the UI — use service.
Services hang out in the background, processing, downloading or uploading data while the phone is locked or the use is using unrelated apps.

There are 3 ways to start a service:
(1) Start — The most straightforward way to start a service is to call the startService() method from a context, such as an activity, using startService(). The service will execute, but will not usually communicate back to the component which started it.

(2) Schedule — If you want to have a service execute at some point in the future, or when some conditions are met, you can create a special type of service called a JobService. You can create your JobService and then you define when the job should run using a Scheduler, like JobScheduler or FireBase JobDispatcher. They allow you to define complex run schedules for your service, like have a service run every 24 hours when on WiFi. Then the Scheduler schedules the job for you.

(3) Bind — Service as “Server”. Component as “Client”. bindService() offers a client-server like interface. With your service being the server and the various components binding to the service being the clients. Components bind to the service using the bindService() method. Unlike started services, which you tend to start and then forget about them. Bound services can easily communicate back to the components that are bound to them.
Ex: Media player application — You might have a bound service which plays audio and an activity which controls the UI. It’s important to use a bound service as oppose to a started service because you want to update the UI to represent where in the audio you are. Pressing one of the buttons in the UI might operate additional commands to the service from the activity.

Note that a service being bound or started is not manually exclusive. A service can be both. Services can be both Started & Bound.

Читайте также:  Как скрыть системные уведомления андроид

Services are great for long running background tasks. All Android core components start on the main thread, including services, and they may block our UI if we run a long running task.

To get around this, we need to create a background thread or AsyncTask from within our service.

Services, like activities, have a lifecycle. Their life cycle is different from an activity’s. For a simple started service, the service is created when a context, such as an activity, calls startService() passing in an intent. It triggers the service’s OnCreate() method. After that onStartCommand() is called. And in onStartCommand() method is where you should actually do whatever it is that your service does (like start an AsyncTask). When the service is done we can signal this by calling stopSelf(). stopSelf() will then trigger onDestroy() and the service will be destroyed. So if we’re using a service class we can create an AsyncTask or thread in oStratCommand(). Inside the AsyncTask/thread we can run our service code.

startService() → onCreate() → onStartCommand() → Service Running → stopSelf() → onDestroy() → Service Shut down

Running time consuming tasks from a service in a background thread is a fairly common thing to do — so we just can use Android’s subclass of service IntentService.

2. Intent Services
IntentService is a service that actually runs in a separate background thread altogether.

To start IntentService we can create a class that extends from IntentService. Inside that class we will write our IntentService should do in the background by overriding the onHandlerIntent() method. Then we simply create an intent and pass in that service class. To start it we just call startService() passing in our intent.

Once startService() is called it launches an IntentService, runs the code inside the onHandleIntent() on a background thread, and then stops itself when it’s done.

The intent object passed into the onHandleIntent() method is the same one we passed in startService(). It means we could attach extra data to that intent when creating it and then we can access that data inside onHandleIntent() and use it.

It’s important to note that all IntentService requests are handles in a single background thread, and they are issued in order. Each IntentService will take as long as necessary and will not block the application’s main thread.

But only one call to the IntentService’s onHandleIntent() method will be processed at a time.

This makes IntentService great for tasks that need to occur in order.

One way to change the UI info for the user in a dynamic way is by using String Pluralization.

3. Notifications
From JellyBean version notifications are able to include actions. Which let us define 2 to 3 specific actions to perform on the data included in the notification.

Nout changes the layout of notification to always show the app posting them.

Android support library wraps almost all of the platform differences with an easy to use API.

Make sure to use notifications wisely — There’s a fine line between useful and spammy.

Notifications in Oreo
Notifications channels — Fine grained notification control for users, with a consistent settings UI. Also called notifications categories. Every notification in Android O needs a channel. We can organize notifications channels into channels groups, which group channels in the UI.

Notifications badges — Badges or dots that appear on apps which show the user that there are outstanding notifications.

We can also set the background color for our foreground notifications, using setColorize().

Pending Intents
What should we want another application to launch an activity in our app, like in the case of notifications? Any notification in Android is displayed by a system service called NotificationManager.

Читайте также:  Fork player android tv apk

A system service is a service that starts by the Android system itself, and is not part of your application processes at all, which means, if you want the NotificationManager to be able to launch an activity in your app, you need to have the permissions to do it by a PendingIntent.

PendingIntents is a wrapper around a regular intent, that is designed to be used by another application. The PendingIntent gives that application the ability to perform the included action, as if it was your app, with all the permissions your app has been granted.

It can launch services, private activities and broadcast protected intents, even if our app is no longer running, like opening a closed app.

To create a PendingIntent instancer we can use one of these following static methods: getActivity(), getService() or getBroadcast().

Adding Notifications
To add a notification to our code we will do the following steps:
(1) In out Utilities class we will create 3 methods:
(1A) One static method that will return our PendingIntent to re-launch the app
(1B) One static largeIcon() method to decode the large icon for the notification
(1C) Another static method to create the actual notification using the NotificationCompat.Builder, and making it to work by using NotificationManager.notify().
(2) In the Android Manifest we will:
(2A) Add the vibrate permission
(2B) Change the launchMode of our main activity to be “singleTop”

Notification Actions
Notifications can take up to 3 actions buttons, which appear in the notification.
Each action button can launch a different pendingIntent and perform different actions.
Actions are optional but we always should allow users to launch the app from pressing our notification.

To add actions to a notification, we call the addAction() on our builder, up to 3 times.

4. Foreground Services and Scheduling Jobs
Foreground service is a special type of service. They used to show the use the real time progress of a long running operation.

Foreground service is a service that the user is actively aware of via a non-dismissible ongoing notification.

Foreground services enable the user to do interactions with our service.

Because Foreground services consider to be important to the user, Android will prioritize foreground services even if the system is memory constrained.

Ex for foreground services: Music that plays in the background or navigation app.

Application Priority
App priority is divided into 4 general buckets:
(1) Critical — Active apps, Foreground Processes
(2) High — Visible Activities
(3) Medium — Service Processes
(4) Low — Background Process, Empty Processes (Stopped Component)

The apps are prioritized in a queue with the app that’s been at the lowest priority for the longest is the first in line to be executed.

The system tries to keep services alive and will even try to restart sticky services.

Killing empty apps first because keeping allocated memory around is cheap, while restarting apps can be relatively expensive. They will be killed in a last seen, first killed order.

The 3 laws of Android Resource Management:
(1) Android will keep all apps that interact with the user running smoothly.
(2) Android wukk keep all apps with visible activities followed by services running, unless doing so violates the first law.
(3) Android will keep all apps in the background running, unless this violates the first or the second laws.

Scheduling Jobs
JobScheduler was introduced on Android L. JobScheduler allows us to simply grab a reference to a jobScheduler object from the context, create a new job with all the constraints you wanted, and then schedule the job whenever we want by calling the schedule() method.

FirebaseJobDispatcher is a more compatible version of JobScheduler.
JobScheduler provides compatibility back to API 21 whereas FirebaseJobDispatcher provides compatibility back to API 9 Gingerbread.

To use FirebaseJobDispatcher, we first need to make a GooglePlayDriver and use that to create a new FirebaseJobDispatcher object. Then you build the job, specifying all the constraints.

Читайте также:  Square home launcher для андроид тв

We can set when the job executes by using the setTrigger() method and passing it an execution window. The time applied here are in seconds. This method assume that all of the other constraints are met. Why use a window? Battery life is saved if the phone can do a bunch of processing, especially network calls all in a batch. This allows the phone to stay more continuously in a low power state if the user is not actively using the phone, which means more battery life. Supplying a window lets the system pick the best time to schedule your job in that window.

Steps to add a FirebaseJobDispatcher:
(1) Add the gradle dependency
(2) Create a new task in our Helper tasks class
(3) Create a new service that extends from JobService
(4) Add the JobServiced to the manifest
(5) Schedule with FirebaseJobDispatcher

5. Broadcast Receiver
System Broadcast Intent — A special intent sent by the system when events occur on the phone (Ex.Screen on, headset plug, battery low, etc.)

When we create an app that needs to know about one of these state changes on the phone and then run some code, you’re going to be using a broadcast receiver.

Broadcast Receiver — Core android component that enables applications to receive intents that are broadcast by the system or by other applications. A broadcast receiver can be triggered even when the app is not running.

For example, when we need to know when our connectivity changes from offline to online we would use a broadcast receiver to be triggered when the change has occurred.

You specify which request the receiver is interested in using an intent filter.

Intent Filter — Expression that says what intents should trigger your component. They are not specific to broadcast receivers. Intent filters are commonly used in the app’s manifest file.

If we want to create a broadcast receiver there are 2 options:
(1) Static broadcast receiver — Triggered whenever the broadcast intent occurs, even if the app is offline. Relying on static broadcast receivers can be problematic. Static receivers registered in the app’s manifest and using the intent filter tags.
(2) Dynamic broadcast receiver — Tied to the app’s lifecycle. Better to use this one or a JobScheduler. We register and unregister the receiver during the activity lifecycle methods (like onResume() and onPause()). This receiver will listen only when our activity is open and visible.

Some broadcast intents won’t let you statically define a broadcast receiver to avoid cases of overwhelming use by many apps using that receiver on the same time. Specifically, intents that have the FLAG_RECEIVER_REGISTERED_ONLY flag set.

If we need to execute code when our app isn’t running:
(*) We should use JobDispatcher if we can
(*) In some cases we need to use a static broadcast receiver (when we need to catch certain specific intents on older devices when the app is not running)

If our app is already started and running:
(*) We should use a dynamic broadcast receiver.

Steps to add a dynamic broadcast receiver:
(1) Add method that will do the action we want when the broadcast receiver is triggered
(2) Make an intent filter variable and instantiate it in onCreate()
(3) Make the broadcast receiver class and override onReceive() that updates the UI
(4) Register broadcast receiver with intent filter in onResume() using registerReceiver()
(5) Cleanup broadcast receiver in onPause() using unregisterReceiver()

You can find much more at the free course Developing Android Apps by Google on the Udacity website over here: (check it out!)

Источник

Оцените статью