Introduction
If you are looking at what exactly limitations are applied in background service in Android in Oreo. You are in the right place. In the previous article, I have explained, What is Service and Types of Service. In this article, I’m gonna explain, what exactly limitations are applied in background service in Android in Oreo.
Understand Background and Foreground service
Before moving forward lets clear some terminology, What is background service what is foreground service? The app said to be in the foreground when the app is visible, that is its activity is visible and the user is interacting with the activity. Such as we open the Facebook application so the Facebook application comes in the foreground now some other criteria is when the application has a service running in the foreground.
So if either of the above-mentioned criteria fails then the application is said to be in the background. Let me explain it with an example, suppose in your device, no one app is recently open mean recent app stack is clear now. Now your friend sends you WhatsApp message. One notification will come in WhatsApp mean, WhatsApp application is acting as a background application that is it has not opened it and it has no visibility now. Now launch this WhatsApp application then you get landing right so when the application becomes visible then the WhatsApp application is said to be in the foreground state. In simple words, we can say that when the application is not visible it is in background and also when the application is visible it is said to be in the foreground
Limitation on Background Service
Starting from Android Oreo( API 26) and onwards the background applications(when an application is not foreground ) cannot use the started service. When you call startService() method from the background applications simply through the IllegalStateException. In other words, you can say if you call startService() when your application is not in the foreground.
Why so limitation? Just because running a service in the background consumes a lot of memory. this simply impacts the device performance thus resulting in that make the quick battery drain and degrade the User experience.
The following service you can run in Oreo (API 26)
- Bounded Service
- Foreground Service
- When the application is currently in the foreground
In case you call startService() method while app is foreground after that application moves to the background, after small amount of time service also shutdown
For better clarity, We will take an example of IntentService and apply below use cases.
Use Case
Now I will create an IntentService, will run it in the foreground as well as background and run also Pre Oreo and Oreo device also.
Create a new application
Let’s create a new application with Empty Activity template.
Create a subclass of IntentService and add block of code
package com.wave.backgroundserviceexample; import android.app.IntentService; import android.content.Intent; import android.os.Handler; import android.support.annotation.Nullable; import android.util.Log; import android.widget.Toast; public class CounterService extends IntentService { final Handler mHandler = new Handler(); private static final String TAG = "CounterService"; /** * provide name of worker thread */ public CounterService() { super(TAG); } @Override public void onCreate() { super.onCreate(); showToast("Job Execution Started"); } @Override protected void onHandleIntent(@Nullable Intent intent) { int maxCount = intent.getIntExtra("maxCountValue", -1); /** * Suppose we are performing task 1 to 1000, Each task will takes time 1 sec , So You saw we sleep thread or one second. */ for (int i = 0; i < maxCount; i++) { Log.d(TAG, "onHandleWork: The number is: " + i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } // Helper for showing tests void showToast(final CharSequence text) { mHandler.post(new Runnable() { @Override public void run() { Toast.makeText(CounterService.this, text, Toast.LENGTH_SHORT).show(); } }); } }
After that Open actvitiy_main.xml and add below code
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.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" tools:context=".MainActivity"> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:layout_marginEnd="8dp" android:layout_marginBottom="8dp" android:background="@color/colorAccent" android:onClick="onStartIntentService" android:padding="16dp" android:text="Start IntentService" android:textAllCaps="false" android:textColor="#fff" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </android.support.constraint.ConstraintLayout>
Let’s move to MainActivity and add below code
package com.wave.backgroundserviceexample; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void onStartIntentService(View view) { Intent mIntent = new Intent(MainActivity.this, CounterService.class); mIntent.putExtra("maxCountValue", 1000); startService(mIntent); } }
Now above code, we have created a JobIntentService In this service we are printing 1 to 1000 no in one-second interval. In MainActivity we expose only onClick methods. In this method, We are starting IntentService with the help of startService() method. Now run this project.
Use Case -1 App Foreground and API level smaller than equal to 25 (Pre Oreo)
Build the project and run in pre oreo devices, and click the Start IntentServce button. Button event will be triggered and service is started. You can verify it with Logcat. Output like below
D/CounterService: Job Execution Started D/CounterService: onHandleWork: The number is: 0 D/CounterService: onHandleWork: The number is: 1 D/CounterService: onHandleWork: The number is: 2 D/CounterService: onHandleWork: The number is: 3 D/CounterService: onHandleWork: The number is: 4 ...
UseCase -2 App Foreground and API level 26 or onward (Oreo or P)
Run the project in Oreo devices and click on Start IntentServce button. The result as same as UseCase-1.
UseCase -3 App Background and API level smaller than equal to 25 (Pre Oreo)
Now testing this use case we have to start the service when the app is background. For running an app in the background, We have to create a receiver with a BootComplete receiver. create a new class like below
package com.wave.backgroundserviceexample; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class BootCompleteReceiver extends BroadcastReceiver { @Override public void onReceive(Context mContext, Intent intent) { if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { Intent mIntent = new Intent(mContext, CounterService.class); mIntent.putExtra("maxCountValue", 1000); mContext.startService(mIntent); } } }
Now register this recevier in manifest
<receiver android:name=".BootCompleteReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver>
Run the project and restart the device. While the device will be booted, onReceive() will call and startService() method will execute. The output result will be expected. Mean IntentService will work perfectly in the pre Oreo device.
UseCase – 4 App Background and API level 26 or onward (Oreo or P).
In this use case run the same project in Oreo devices and above and restart the device. The application has crashed in the Oreo device. that means we cannot start the IntentService from the background application in the Oreo device.
The possible solution for dealing with Service in Oreo and onward
- Job Scheduler
- Firebase Cloud Messaging and Service Whitelist
- Start Service in Foreground
- JobIntentService (internally used Job Scheduler API )
2 Comments
Neat and clean explanation..Thanks for your effort…I am your follower..I am having small doubt is that ” In other words, you can say if you call startService() when your application is not in the foreground.” this statement is right.
Really Nice explanation by the author for getting the idea on limitation of background service for Oreo . Searching for the same since few weeks . But , I am having the same doubt about the statement what Kajendhiran R mentioned in his comment ..