Handle Android Services (Part-1)

Abhishek Srivastava

--

With recent Android releases dealing with background Services, it has become more complicated than ever. For android developers there are Five groundbreaking changes:

>Memory Basics
>Background execution limits
>Location updates limit
>Removing of implicit broadcasts
>Notification channels

Memory Basics: Why Android devices have out of memory issue on Mobile Apps due to the heavy load of running Activity/Services

Android kernel was first developed it and it was based on Linux-Kernel. The main difference between Android and all other Linux-Kernel based systems is that Android does not have a thing called “Swap space.”

Swap space in Linux is used when the amount of physical memory (RAM) is full. If the system needs more memory resources and the RAM is full, inactive pages in memory are moved to the swap space. While swap space can help machines with a small amount of RAM, it should not be considered a replacement for more RAM. Swap space is located on hard drives, which have a slower access time than physical memory.

In Android, there is no such thing as a “Swap space.” When the system is running out of memory. The guy’s goal is to free up memory by killing the processes based on their “visibility state” and the amount of memory consumed.

Every process gave its oom_adj score by Activity Manager. It’s a combination of application state (e.g., foreground, background, background with service, etc.). Here is a short example of all oom_adj values:

# Define the oom_adj values for the classes of processes that can be
# killed by the kernel. These are used in ActivityManagerService.
setprop ro.FOREGROUND_APP_ADJ 0
setprop ro.VISIBLE_APP_ADJ 1
setprop ro.SECONDARY_SERVER_ADJ 2
setprop ro.BACKUP_APP_ADJ 2
setprop ro.HOME_APP_ADJ 4
setprop ro.HIDDEN_APP_MIN_ADJ 7
setprop ro.CONTENT_PROVIDER_ADJ 14
setprop ro.EMPTY_APP_ADJ 15

Background execution limits

Let’s talk about the background execution limitations. Background execution limitations mainly apply to two major components:
>Service
>Wake locks

Service:

A Service is an application component that can perform long-running operations in the background, and it does not provide a user interface.

So, fundamentally Service is the same thing as the activity but it doesn’t have the UI component in it. So, it doesn’t have to perform smooth animation at 60 fps. That’s why it can run perform any task for a longer period of time than the activity.

There are three types of service:
Started Service — A service is started when an application component (such as an activity) calls startService().
Bound Service — A service is bound when an application component binds to it by calling bindService().
Scheduled Service — A service is scheduled when an API such as the JobScheduler.

Background vs Foreground applications:

To learn background execution changes, we need to know the difference between background and foreground application first.

Rule of thumb, your application will be considered as a foreground service if any of the below three cases are true:

1. Your application has currently visible activity.
2. Your application has foreground service running.
3. Your application is connected to another foreground app by binding the service or by consuming their content providers.

If any of the above scenarios are not true in the current instance, your application is considered to be in the background.

Why do we need to restrict the use of background services?
Whenever your applications run in the background using services, your application consumes two precious resources: 1) Memory and 2) Battery.

These two are limited resources on mobile devices and most of the low to mid-range devices don’t have plenty of memory or battery inside it.

Suppose, if your application is doing some very intensive tasks in the background and using the larger amount of RAM to perform that task, then this will create the very junky user experience, especially if the user is using another resource-intensive app, such as playing a game or watching a video in foreground.

As per the documentation for the started service the best practice is,
When the operation is complete, the service should stop itself.
But, many applications have long-running background services, which basically runs for the infinite time to either maintain the socket connection with the server or monitor some tasks or user activity. These services create battery drain and also they constantly consume memory.

What are the limitations of services starting from Android O?

Starting from Android O, if your application is in the background (check above three conditions), your application is allowed to create and run background services for some minutes.
After some minutes passed, your application will enter the idle stage. When your application entered in the idle stage, the system will stop all the background services just like your service calls Service.stopSelf().
And here comes the fun part…
As I discussed above, the problem of battery drain and memory consumption is mainly caused by started services. To eliminate this, Android O completely prevents the use of the startService() method to start the service. If you call startService() on Android O, you will end up getting IllegalArgumentException.

To handle Background process execution and memory issue Google launches WorkManager.

WorkManager

WorkManager aims to simplify the developer experience by providing a first-class API for system-driven background processing. It is intended for background jobs that should run even if the app is no longer in the foreground. Where possible, it uses JobScheduler or Firebase JobDispatcher to do the work; if your app is in the foreground, it will even try to do the work directly in your process.WorkManager incorporates the features of its predecessors in a modern, consistent API that works back to API level 14 while also being conscious of battery life.

WorkManager uses an underlying job dispatching service based on the following criteria:

Note: If your app targets Android 10 (API level 29) or above, your FirebaseJobDispatcher and GcmNetworkManager API calls will no longer work on devices running Android Marshmallow (6.0) and above. Follow the migration guides for FirebaseJobDispatcher and GcmNetworkManager for guidance on migrating. Also, see the Unifying Background Task Scheduling on the Android announcement for more information regarding their deprecation.

The WorkManager library has several components:

WorkManager — receives work with arguments & constraints and enqueues it.

Worker — have only one method to implement doWork() which is executed on a background thread. It’s the place where all your background tasks should be done. Try to keep it as simple as possible.

WorkRequest — work request specify which Worker enqueued with what Arguments and what are the Constraints for it (e.g., internet, charging ).

WorkResult — Success, Failure, Retry.

Data — Persistable set of key/value pairs which are passed to/from Worker.

Underneath, the architecture of WorkManager lib will look like this:

Now for the rest points, we will discuss in the second part for Location update Issue, Removing of implicit broadcasts, and Notification channels.

https://developer.android.com/topic/libraries/architecture/workmanager

--

--

Abhishek Srivastava

Senior Software Engineer | Android | Java | Kotlin | Xamarin Native Android | Flutter | Go