From Service to WorkManager: Understanding Android’s New Architecture Components

From Service to WorkManager: Understanding Android's New Architecture Components

Hot Article Guide | Click the title to read

Android Architecture Advanced Learning Roadmap

Reject the routine, get the video from Programmer to Architect in Android

New Android Architecture Component: WorkManager

Author: ronaldong

Link: https://juejin.im/post/5b04d064f265da0b80711759

With the continuous updates of Android versions, how to properly handle background tasks has become increasingly complex. Therefore, Google released WorkManager (as part of JetPack) to help developers solve this problem.

Before learning about WorkManager, we first need to understand why we need it. This article will explain from the following three parts:

  1. Basic Knowledge Related to Android System Memory

  2. Existing Solutions

  3. WorkManager

1. Android Memory

The Android system kernel is based on the Linux kernel, and the main difference between it and other Linux-based systems is that the Android system does not have swap space. When the system memory resources are exhausted, but there are additional memory resource requests, inactive pages in memory will be moved to swap space. Swap space is an area on the disk, so its access speed is slower than physical memory.

From Service to WorkManager: Understanding Android's New Architecture Components

In light of this, the Android system introduced OOM (Out Of Memory) Killer to solve the problem of exhausted memory resources. Its role is to determine whether to kill a process based on the amount of memory consumed by the process and the process’s visibility state, thereby freeing up memory.

The Activity Manager sets corresponding oom_adj values for processes in different states. Here are some examples:

# 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    //Foreground process
    setprop ro.VISIBLE_APP_ADJ 1       //Visible process
    setprop ro.SECONDARY_SERVER_ADJ 2  //Secondary service
    setprop ro.BACKUP_APP_ADJ 2        //Backup process
    setprop ro.HOME_APP_ADJ 4          //Desktop process
    setprop ro.HIDDEN_APP_MIN_ADJ 7    //Background process
    setprop ro.CONTENT_PROVIDER_ADJ 14 //Content provider
    setprop ro.EMPTY_APP_ADJ 15        //Empty process

The larger the process’s oom_adj value, the greater the likelihood it will be killed by the OOM killer. The OOM killer kills processes based on a combination of the system’s free memory space and the oom_adj threshold. For example, when the free memory space is less than X1, it kills those processes with an oom_adj value greater than Y1. Its basic processing flow is shown in the figure below:

From Service to WorkManager: Understanding Android's New Architecture Components
From Service to WorkManager: Understanding Android's New Architecture Components

By now, I hope you understand two points:

  1. The less memory your application consumes, the less likely it is to be forcibly killed by the system, meaning your application will live longer.

  2. You must clearly understand the various states of the application. When your application goes to the background but needs to continue executing tasks, you must use Service.

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

The reasons for using service are as follows:

  1. Tell the system that your application has a task that needs to be executed for a long time and obtain the corresponding oom_adj value for the task’s process.

  2. It is one of the four main components of Android applications (the other three components are BroadcastReceiver, Activity, and ContentProvider).

  3. Service can run in an independent process.

However, there is also a drawback: I developed my first application, which actually drained the battery from 100% to 0% in less than 3 hours, because my application opened a service: fetching data from the server every 3 minutes.

From Service to WorkManager: Understanding Android's New Architecture Components

At that time, I was just a young, inexperienced developer. But for some reason, even after 6 years, many unknown applications are still doing the same thing.

From Service to WorkManager: Understanding Android's New Architecture Components

Every developer can perform any operations they want in the background without restriction. Google also realized this and tried to take some improved measures.

Starting from Marshmallow, followed by Nougat, the Android system introduced Doze mode

From Service to WorkManager: Understanding Android's New Architecture Components

What is Doze mode? In short, when the user turns off the phone screen, the system will automatically enter Doze mode, prohibiting all applications’ network requests, data synchronization, GPS, alarms, wifi scanning, etc., until the user lights up the screen again or the phone is connected to power, effectively saving the phone’s battery.

But this feels like a drop in the bucket, so starting from Android Oreo (API 26), Google made further improvements: if an application’s target version is Android 8.0, when it calls the Service’s startService() method in scenarios where creating background services is not allowed, this method will throw an IllegalStateException. This issue can be resolved by adjusting the targeting SDK value; some well-known applications have a target API of 22 because they are reluctant to handle runtime permissions.

However, next you will find:

  • August 2018: All newly developed applications must have a target API level of 26 (Android 8.0) or higher.

  • November 2018: All released applications must update their target API level to 26 or higher.

  • Starting from 2019: After each new version of the Android system is released, all newly developed and pending updated applications must adjust their target API level to the corresponding system version or higher within a year.

Having said so much – (I believe you will come to the same conclusion):

The Service we know has been deprecated because it is no longer allowed to run long-running operations in the background, which was its original purpose.

Except for Foreground service, we have no reason to use Service anymore.

2. I have a network call. What is out there:

First, let’s take a simple example: there is a simple network request that can download several kilobytes of data. The simplest method (which is not the correct method) is to open a separate thread to perform this request.

int threads = Runtime.getRuntime().availableProcessors();
ExecutorService executor = Executors.newFixedThreadPool(threads);
executor.submit(myWork);

Now consider the login scenario. The user fills in the email and password, then clicks the login button. The user’s phone is on a 3G network with a poor signal, and then they walk into an elevator.

From Service to WorkManager: Understanding Android's New Architecture Components

When the application is executing the login network request, the user receives a call.

OkHttp’s default timeout is very long

connectTimeout = 10_000;
readTimeout = 10_000;
writeTimeout = 10_000;

Usually, we set the number of retries for network requests to be 3

So, in the worst-case scenario: 3 * 30 = 90 seconds.

Now please answer a question —

Was the login successful?

When your application goes to the background, you don’t know anything. As we understand, you cannot expect your application process to survive to complete the network request, handle the response, and save the user’s login information. Not to mention the user’s phone may also enter offline mode, losing network connection.

From the user’s perspective, I have entered my email and password and clicked the login button, so I should have logged in successfully. If I haven’t logged in successfully, the user would think your application’s user experience is very poor, but in fact, this is not a user experience issue, but a technical issue.

Next, you may think, okay, once my application is about to go to the background, I will open a Service to perform the login operation, but you can’t!!!

At this point, JobScheduler can come in handy.

ComponentName service = new ComponentName(this, MyJobService.class);
JobScheduler mJobScheduler = (JobScheduler)getSystemService(
            Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = new JobInfo.Builder(jobId, serviceComponent)
 .setRequiredNetworkType(jobInfoNetworkType)
 .setRequiresCharging(false)
 .setRequiresDeviceIdle(false)
 .setExtras(extras).build();
mJobScheduler.schedule(jobInfo);

JobScheduler first schedules a task, and then at the appropriate time (for example, after a delay, or when the phone is idle), the system will start your MyJobService and execute the logic in onStartJob(). This idea is theoretically good, but it is only available on systems with API > 21, and there is a major bug in JobScheduler on API 21 & 22.

From Service to WorkManager: Understanding Android's New Architecture Components
From Service to WorkManager: Understanding Android's New Architecture Components
From Service to WorkManager: Understanding Android's New Architecture Components

This means you can only use JobScheduler on systems with API > 22.

From Service to WorkManager: Understanding Android's New Architecture Components

If your application’s minSDK < 23, you can use JobDispatcher.

Job myJob = firebaseJobDispatcher.newJobBuilder()
 .setService(SmartService.class)
 .setTag(SmartService.LOCATION_SMART_JOB)
 .setReplaceCurrent(false)
 .setConstraints(ON_ANY_NETWORK)
 .build();
firebaseJobDispatcher.mustSchedule(myJob);

Wait, it requires Google Play Services!!

So if you plan to use JobDispatcher, you will abandon tens of millions of users.

From Service to WorkManager: Understanding Android's New Architecture Components

Therefore, JobDispatcher may not be a good choice. What about AlarmManager? Use AlarmManager to poll check whether the network request is successful, and if not, try to execute it again?

If you still want to use Service to execute the network request immediately, you can choose JobIntentService

From Service to WorkManager: Understanding Android's New Architecture Components

When SDK < 26, use IntentService to execute tasks; when SDK ≥ 26, use JobScheduler to execute tasks.

Ahhhh… it cannot execute requests immediately on Android Oreo.

So back to where we started: when the application goes to the background, choose the appropriate task scheduler to schedule and execute background tasks based on the version of the Android system and the state of the phone.

Goodness, it is really difficult to both save the phone’s battery and provide users with an amazing user experience!

3. WorkManager. Just because work should be easy to do.

Depending on the phone’s state, Android system version, and whether the phone has Google Play Services, you can choose the corresponding solution. You might try to implement this entire complex processing logic yourself. The good news is that the designers of the Android framework have heard our complaints, and they decided to solve this problem.

On the last Google I/O Android framework, the team announced 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.

Wow! This is exactly what we need!

The WorkManager library contains the following components:

WorkManager receives WorkRequests with parameters and constraints and queues them.

Worker You only need to implement the doWork() method, which is executed in a separate background thread. All tasks that need to be executed in the background are completed in this method.

WorkRequest Sets parameters and constraints (such as whether there is a network connection, whether the power is connected) for the Worker.

WorkResult Success, Failure, Retry.

Data The persistent key-value pairs passed to the Worker.

First, create a class that inherits from Worker and implement its doWork() method:

From Service to WorkManager: Understanding Android's New Architecture Components

Then use WorkManager to queue it:

From Service to WorkManager: Understanding Android's New Architecture ComponentsNext, WorkManager will reasonably schedule and execute your tasks; it will store all task parameters, task details, and update task status. You can even use LiveData to subscribe to observe its status changes:

From Service to WorkManager: Understanding Android's New Architecture Components

The architecture diagram of the WorkManager library is as follows:

From Service to WorkManager: Understanding Android's New Architecture Components

It can do more than that.

You can use it to perform scheduled tasks:

Constraints constraints = new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build();
PeriodicWorkRequest locationWork = new PeriodicWorkRequest.Builder(LocationWork.class, 15, TimeUnit.MINUTES).addTag(LocationWork.TAG)
.setConstraints(constraints).build();
WorkManager.getInstance().enqueue(locationWork);

You can also let multiple tasks execute in order:

WorkManager.getInstance(this)
.beginWith(Work.from(LocationWork.class))
.then(Work.from(LocationUploadWorker.class))
enqueue();

You can also let multiple tasks execute simultaneously:

WorkManager.getInstance(this).enqueue(Work.from(LocationWork.class, LocationUploadWorker.class));

Of course, you can also combine the above three task execution methods.

Note: You cannot build a task chain that mixes scheduled tasks and one-time tasks.

WorkManager can do a lot of things: cancel tasks, combine tasks, build task chains, merge the parameters of one task into another task. I recommend you to check the official documentation, which has many good examples.

Summary

To follow the principle of saving user phone battery, every version of Android is constantly improving, making handling background tasks very complex. Thanks to the Android team, we can now use WorkManager to handle background tasks more simply and directly.

If you have good articles to share, feel free to submit them directly to me.

Finally, welcome everyone to join our Knowledge Planet, this period ends on March 10, 2019, so the sooner you join, the better. The current number of friends joining is nearly 1000, and when it reaches 1000, the price will increase significantly (there are only a few spots left), so hurry up!

From Service to WorkManager: Understanding Android's New Architecture Components

Scan the WeChat QR code or click the QR code above to receive advanced resources for Android, Python, AI, Java, etc.

For more learning materials, click the “Read Original” below to get them.

From Service to WorkManager: Understanding Android's New Architecture Components

Leave a Comment

×