480

On application launch, app starts the service that should to do some network task. After targeting API level 26, my application fails to start service on Android 8.0 on background.

Caused by: java.lang.IllegalStateException: Not allowed to start service Intent { cmp=my.app.tt/com.my.service }: app is in background uid UidRecord{90372b1 u0a136 CEM idle procs:1 seq(0,0,0)}

as I understand it related to: Background execution limits

The startService() method now throws an IllegalStateException if an app targeting Android 8.0 tries to use that method in a situation when it isn't permitted to create background services.

"in a situation when it isn't permitted" - what it's actually mean?? And how to fix it. I don't want to set my service as "foreground"

Vadim Kotov
8,2848 gold badges51 silver badges63 bronze badges
asked Sep 27, 2017 at 10:14
13
  • 9
    It means that you cannot start a service when your app is in the background Commented Sep 27, 2017 at 10:17
  • 25
    this has nothing to do with runtime permissions Commented Sep 27, 2017 at 10:22
  • 28
    Use startForegroundService() instead of startService(). Commented Oct 11, 2017 at 13:21
  • 2
    You can try to use targetSdkVersion 25 but compile with compileSdkVersion 26. This way you can use new classes from Android 8 and newest suppport library but your app will not be limited by Background Execution Limits. Commented Jan 19, 2018 at 10:18
  • 4
    @KacperDziubek That should work but is a temporary solution as it will be required to target SDK26 in fall of 2018. Commented Jun 8, 2018 at 21:04

18 Answers 18

344

I got solution. For pre-8.0 devices, you have to just use startService(), but for post-8.0 devices, you have to use startForgroundService(). Here is sample for code to start service.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
 context.startForegroundService(new Intent(context, ServedService.class));
} else {
 context.startService(new Intent(context, ServedService.class));
}

And in service class, please add the code below for notification:

@Override
public void onCreate() {
 super.onCreate();
 startForeground(1,new Notification());
}

Where O is Android version 26.

If you don't want your service to run in Foreground and want it to run in background instead, post Android O you must bind the service to a connection like below:

Intent serviceIntent = new Intent(context, ServedService.class);
context.startService(serviceIntent);
context.bindService(serviceIntent, new ServiceConnection() {
 @Override
 public void onServiceConnected(ComponentName name, IBinder service) {
 //retrieve an instance of the service here from the IBinder returned 
 //from the onBind method to communicate with 
 }
 @Override
 public void onServiceDisconnected(ComponentName name) {
 }
}, Context.BIND_AUTO_CREATE);
Aaron Meese
2,3537 gold badges30 silver badges48 bronze badges
answered Dec 5, 2017 at 12:53
Sign up to request clarification or add additional context in comments.

15 Comments

A foreground service is something that the user will be aware of and that needs a notification. It will also ANR if it runs too long. So it's not really a suitable answer if the app is already running in the background.
There is a ContextCompat.startForegroundService(...) in support lib that can be used instead.
I also agree that this is not a solution. It's a workaround and it helps, but the background limits in Oreo were introduced for a reason. Bypassing those limits this way definitely isn't the correct approach (even though it works). The best way is to use JobScheduler (refer to the accepted answer).
I don't think it will be a good user experience if you must show an empty foreground notification. Considering the fact that u must. -- Android 8.0 introduces the new method startForegroundService() to start a new service in the foreground. After the system has created the service, the app has five seconds to call the service's startForeground() method to show the new service's user-visible notification. If the app does not call startForeground() within the time limit, the system stops the service and declares the app to be ANR.
It seems that new Notificaction() doesn't work from Android 8.1; You should create the channel for notification: stackoverflow.com/a/47533338/1048087
|
226
+50

The permitted situations are a temporary whitelist where the background service behaves the same as before Android O.

Under certain circumstances, a background app is placed on a temporary whitelist for several minutes. While an app is on the whitelist, it can launch services without limitation, and its background services are permitted to run. An app is placed on the whitelist when it handles a task that's visible to the user, such as:

  • Handling a high-priority Firebase Cloud Messaging (FCM) message.
  • Receiving a broadcast, such as an SMS/MMS message.
  • Executing a PendingIntent from a notification.
  • Starting a VpnService before the VPN app promotes itself to the foreground.

Source: https://developer.android.com/about/versions/oreo/background.html

So in other words if your background service does not meet the whitelist requirements you have to use the new JobScheduler. It's basically the same as a background service, but it gets called periodically instead of running in the background continuously.

If you're using an IntentService, you can change to a JobIntentService. See @kosev's answer below.

JohnnyLambada
12.9k12 gold badges60 silver badges61 bronze badges
answered Sep 27, 2017 at 10:22

10 Comments

I am getting a crash after I want to start service just after I receive GCM message of "high" prio. I still use GCM: "com.google.android.gms:play-services-gcm:11.4.2", not 'com.google.firebase:firebase-messaging:11.4.2'. Not sure it matters though..
"It's basically the same as a background service, but it gets called periodically instead of running in the background continuously." - not sure what you mean by this, as Android services have never executed continuously. They start, run, then shut down.
Is the FirebaseInstanceIdService and its onTokenRefresh method a high-priority FCM message?
Shouldn't you use WorkManager (here: developer.android.com/topic/libraries/architecture/workmanager ) instead of JobScheduler or others ? I mean this: youtu.be/IrKoBFLwTN0
SO is pretty good, but I have been misled before. Is it really true that I have to use JobScheduler? Are all the people talking about startForegroundService/startForeground on something?
|
104

The best way is to use JobIntentService which uses the new JobScheduler for Oreo or the old services if not available.

Declare in your manifest:

<service android:name=".YourService"
 android:permission="android.permission.BIND_JOB_SERVICE"/>

And in your service you have to replace onHandleIntent with onHandleWork:

public class YourService extends JobIntentService {
 public static final int JOB_ID = 1;
 public static void enqueueWork(Context context, Intent work) {
 enqueueWork(context, YourService.class, JOB_ID, work);
 }
 @Override
 protected void onHandleWork(@NonNull Intent intent) {
 // your code
 }
}

Then you start your service with:

YourService.enqueueWork(context, new Intent());
answered Apr 15, 2018 at 20:21

6 Comments

How you are able to call a non-static method inside a static method? Can you please explain?
@Maddy enqueueWork(...) is a static method as well.
Where would you call YourService.enqueueWork(context, new Intent()); ? From broadcast receiver?
JobIntentService is deprecated. Better go with this suggestion stackoverflow.com/questions/68696064/…
|
38

If the service is running in a background thread by extending IntentService, you can replace IntentService with JobIntentService which is provided as part of Android Support Library

The advantage of using JobIntentService is, it behaves as an IntentService on pre-O devices and on O and higher, it dispatches it as a job

JobScheduler can also be used for periodic/on demand jobs. But, ensure to handle backward compatibility as JobScheduler API is available only from API 21

CopsOnRoad
271k97 gold badges702 silver badges471 bronze badges
answered Dec 7, 2017 at 10:13

1 Comment

The problem with JobIntentService is that Android can schedule your work rather arbitrarily, and it can't be started implicitly without some tinkering around, unlike an IntentService. See stackoverflow.com/questions/52479262/…
21

Yeah, that's because you can't start services in the background anymore on API 26. So you can start ForegroundService above API 26.

You'll have to use

ContextCompat.startForegroundService(...)

and post a notification while processing the leak.

answered May 7, 2018 at 5:48

1 Comment

The OP specifically said he doesn't want as foreground. This should be put as a comment or as part of a more complete answer.
13

As @kosev said in his answer you can use JobIntentService. But I use an alternative solution - I catch IllegalStateException and start the service as foreground. For example, this function starts my service:

@JvmStatic
protected fun startService(intentAction: String, serviceType: Class<*>, intentExtraSetup: (Intent) -> Unit) {
 val context = App.context
 val intent = Intent(context, serviceType)
 intent.action = intentAction
 intentExtraSetup(intent)
 intent.putExtra(NEED_FOREGROUND_KEY, false)
 try {
 context.startService(intent)
 }
 catch (ex: IllegalStateException) {
 intent.putExtra(NEED_FOREGROUND_KEY, true)
 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
 context.startForegroundService(intent)
 }
 else {
 context.startService(intent)
 }
 }
}

and when I process Intent I do such thing:

override fun onHandleIntent(intent: Intent?) {
 val needToMoveToForeground = intent?.getBooleanExtra(NEED_FOREGROUND_KEY, false) ?: false
 if(needToMoveToForeground) {
 val notification = notificationService.createSyncServiceNotification()
 startForeground(notification.second, notification.first)
 isInForeground = true
 }
 intent?.let {
 getTask(it)?.process()
 }
}
answered Mar 7, 2019 at 6:16

3 Comments

I like your try catch solution. For me it is a solution because sometimes context.startService works in background - sometimes not - this looks like the only best way otherwise you have to implement more code in your main class extending Application and implementing ActivityLifecycleCallbacks and keep track whether the app is in the foreground or background and start your intent accordingly.
Can this exception be Catched?
there is one potential mistake - while context.startService(intent) is being caught by try/catch in try block, there is one more time the same code line repeated in catch block, which could potentially cause a crash.
8

I see a lot of responses that recommend just using a ForegroundService. In order to use a ForegroundService there has to be a notification associated with it. Users will see this notification. Depending on the situation, they may become annoyed with your app and uninstall it.

The easiest solution is to use the new Architecture Component called WorkManager. You can check out the documentation here: https://developer.android.com/topic/libraries/architecture/workmanager/

You just define your worker class that extends Worker.

public class CompressWorker extends Worker {
 public CompressWorker(
 @NonNull Context context,
 @NonNull WorkerParameters params) {
 super(context, params);
 }
 @Override
 public Worker.Result doWork() {
 // Do the work here--in this case, compress the stored images.
 // In this example no parameters are passed; the task is
 // assumed to be "compress the whole library."
 myCompress();
 // Indicate success or failure with your return value:
 return Result.SUCCESS;
 // (Returning RETRY tells WorkManager to try this task again
 // later; FAILURE says not to try again.)
 }
}

Then you schedule when you want to run it.

 OneTimeWorkRequest compressionWork = 
 new OneTimeWorkRequest.Builder(CompressWorker.class)
 .build();
 WorkManager.getInstance().enqueue(compressionWork);

Easy! There are a lot of ways you can configure workers. It supports recurring jobs and you can even do complex stuff like chaining if you need it. Hope this helps.

answered Nov 14, 2018 at 14:28

5 Comments

Currently WorkManager is still alpha.
March 05, 2019 - WorkManager 1.0.0 stable release.
should use WorkManager instead of using interservice or JobIntentService
WorkManager is intended for tasks that are deferrable—that is, not required to run immediately... it might be easiest, however, my app needs a background service that executes the users' requests immediately !
If you require the task to be completed immediately, then you should use a foreground service. The user will see a notification and know that you are doing work. Check out the docs if you need help deciding what to use. They have a pretty good guide for background processing. developer.android.com/guide/background
8

Alternate solution by using JobScheduler, it can start service in background in regular interval of time.

Firstly make class named as Util.java

import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
public class Util {
// schedule the start of the service every 10 - 30 seconds
public static void schedulerJob(Context context) {
 ComponentName serviceComponent = new ComponentName(context,TestJobService.class);
 JobInfo.Builder builder = new JobInfo.Builder(0,serviceComponent);
 builder.setMinimumLatency(1*1000); // wait at least
 builder.setOverrideDeadline(3*1000); //delay time
 builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED); // require unmetered network
 builder.setRequiresCharging(false); // we don't care if the device is charging or not
 builder.setRequiresDeviceIdle(true); // device should be idle
 System.out.println("(scheduler Job");
 JobScheduler jobScheduler = null;
 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
 jobScheduler = context.getSystemService(JobScheduler.class);
 }
 jobScheduler.schedule(builder.build());
 }
 }

Then, make JobService class named as TestJobService.java

import android.app.job.JobParameters;
import android.app.job.JobService;
import android.widget.Toast;
 
 /**
 * JobService to be scheduled by the JobScheduler.
 * start another service
 */ 
public class TestJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
 Util.schedulerJob(getApplicationContext()); // reschedule the job
 Toast.makeText(this, "Bg Service", Toast.LENGTH_SHORT).show();
 return true;
}
@Override
public boolean onStopJob(JobParameters params) {
 return true;
 }
 }

After that BroadCast Receiver class named ServiceReceiver.java

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
 public class ServiceReceiver extends BroadcastReceiver {
 @Override
public void onReceive(Context context, Intent intent) {
 Util.schedulerJob(context);
 }
}

Update Manifest file with service and receiver class code

<receiver android:name=".ServiceReceiver" >
 <intent-filter>
 <action android:name="android.intent.action.BOOT_COMPLETED" />
 </intent-filter>
 </receiver>
 <service
 android:name=".TestJobService"
 android:label="Word service"
 android:permission="android.permission.BIND_JOB_SERVICE" >
 </service>

Left main_intent launcher to mainActivity.java file which is created by default, and changes in MainActivity.java file are

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 Util.schedulerJob(getApplicationContext());
 }
 }

WOOAAH!! Background Service starts without Foreground service

[Edit]: You can use Work Manager for any type of background tasks in Android.

answered Aug 27, 2019 at 19:41

Comments

6

From the firebase release notes, they state that support for Android O was first released in 10.2.1 (although I'd recommend using the most recent version).

please add new firebase messaging dependencies for android O

compile 'com.google.firebase:firebase-messaging:11.6.2'

upgrade google play services and google repositories if needed.

answered Dec 13, 2017 at 6:53

1 Comment

This does not answer the question, nor the question has anything to do with firebase. It should be put as a comment.
6

If any intent was previously working fine when the app is in the background, it won't be the case any more from Android 8 and above. Only referring to intent which has to do some processing when app is in the background.

The below steps have to be followed:

  1. Above mentioned intent should be using JobIntentService instead of IntentService.
  2. The class which extends JobIntentService should implement the - onHandleWork(@NonNull Intent intent) method and should have below the method, which will invoke the onHandleWork method:

    public static void enqueueWork(Context context, Intent work) {
     enqueueWork(context, xyz.class, 123, work);
    }
    
  3. Call enqueueWork(Context, intent) from the class where your intent is defined.

    Sample code:

    Public class A {
    ...
    ...
     Intent intent = new Intent(Context, B.class);
     //startService(intent); 
     B.enqueueWork(Context, intent);
    }
    

The below class was previously extending the Service class

Public Class B extends JobIntentService{
...
 public static void enqueueWork(Context context, Intent work) {
 enqueueWork(context, B.class, JobId, work);
 }
 protected void onHandleWork(@NonNull Intent intent) {
 ...
 ...
 }
}
  1. com.android.support:support-compat is needed for JobIntentService - I use 26.1.0 V.

  2. Most important is to ensure the Firebase libraries version is on at least 10.2.1, I had issues with 10.2.0 - if you have any!

  3. Your manifest should have the below permission for the Service class:

    service android:name=".B"
    android:exported="false"
    android:permission="android.permission.BIND_JOB_SERVICE"
    

Hope this helps.

0xCursor
2,2784 gold badges18 silver badges35 bronze badges
answered Feb 15, 2019 at 19:08

Comments

4

If you are running your code on 8.0 then application will crash. So start the service in the foreground. If below 8.0 use this :

Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
context.startService(serviceIntent);

If above or 8.0 then use this :

Intent serviceIntent = new Intent(context, RingtonePlayingService.class);
ContextCompat.startForegroundService(context, serviceIntent );
answered Sep 18, 2018 at 11:14

2 Comments

It is recommended to only use Foreground services for cases where the user needs to be aware that a service is running. The typical example is for playing music in the background. There are other cases that make sense, but you shouldn't just convert all of your services to Foreground services. Consider converting your services to use WorkManager from Google's Architectural components when you just need to do some work in the background and be guaranteed that it will run.
startForegroundService requires permission, otherwise java.lang.SecurityException: Permission Denial: startForeground from pid=13708, uid=10087 requires android.permission.FOREGROUND_SERVICE. Fix at stackoverflow.com/a/52382711/550471
4

Due to controversial votes on this answer (+4/-4 as of this edit), PLEASE LOOK AT THE OTHER ANSWERS FIRST AND USE THIS ONLY AS A LAST RESORT. I only used this once for a networking app that runs as root and I agree with the general opinion that this solution should not be used under normal circumstances.

Original answer below:

The other answers are all correct, but I'd like to point out that another way to get around this is to ask user to disable battery optimizations for your app (this isn't usually a good idea unless your app is system related). See this answer for how to request to opt out of battery optimizations without getting your app banned in Google Play.

You should also check whether battery optimizations are turned off in your receiver to prevent crashes via:

if (Build.VERSION.SDK_INT < 26 || getSystemService<PowerManager>()
 ?.isIgnoringBatteryOptimizations(packageName) != false) {
 startService(Intent(context, MyService::class.java))
} // else calling startService will result in crash
answered Jul 22, 2018 at 9:13

2 Comments

Asking your users to let you have a free pass using as much battery as possible isn't a good solution. Consider converting your code to a more battery friendly solution. Your users will thank you.
@TALE Not every background service can be made battery friendly using JobScheduler and stuff. Some of the apps need to work at a lower level than typical syncing applications. This is an alternative solution when that doesn't work.
1

if you have integrated firebase messaging push notification then,

Add new/update firebase messaging dependencies for android O (Android 8.0), due to Background Execution Limits.

compile 'com.google.firebase:firebase-messaging:11.4.0'

upgrade google play services and google repositories if needed.

Update:

 compile 'com.google.firebase:firebase-messaging:11.4.2'
answered Oct 4, 2017 at 11:38

Comments

0

Use startForegroundService() instead of startService() and don't forget to create startForeground(1,new Notification()); in your service within 5 seconds of starting service.

cordeiro
1291 silver badge12 bronze badges
answered Mar 21, 2018 at 5:29

1 Comment

It seems that new Notificaction() doesn't work from Android 8.1; You should create the channel for notification: stackoverflow.com/a/47533338/1048087
0

it's actually happening because the phone is on offscreen, or you pressed the power button while starting the service. solution for this which worked for me is to start an activity and when it will go in onResume then start the service. in my case, it was booting up and starting a service.

answered Jan 26, 2021 at 18:36

Comments

0

I am very dissatisfied with the answers here. What if foreground service nor WorkManager fit the use case? I've come to a solution, where I use process scope and make sure to not include scope cancellation exception in the logging logic. Like so:

with(ProcessLifecycleOwner.get()) {
 lifecycleScope.launch {
 lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
 try {
 context.startService(context, Service::class.java)
 } catch (ex: CancellationException) {
 // app minimized, scope cancelled, do not log as error
 } catch (ex: IllegalStateException) {
 logToFirebase(ex)
 }
 }
 }
}

More detailed in this article https://medium.com/@lepicekmichal/android-background-service-without-hiccup-501e4479110f

answered Jun 29, 2022 at 19:41

Comments

0

You may try this code to avoid crash. As google developers said in issue tracker.

private val activityManager by lazy { getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager }
//due to https://issuetracker.google.com/issues/113122354
private fun isInForegroundByImportance(): Boolean {
 val importanceState = activityManager.runningAppProcesses.find {
 it.pid == android.os.Process.myPid()
 }?.importance ?: return false
 return importanceState >= RunningAppProcessInfo.IMPORTANCE_FOREGROUND
}

and usage

override fun onResume() {
 super.onResume()
 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || isInForegroundByImportance()) {
 val intent = Intent(this, BluetoothScannerService::class.java)
 this.startService(intent)
 }
}
answered Nov 28, 2022 at 12:56

Comments

-1

i had this problem too

added this library

implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'

and reinstalled the app solved this for me

answered Jun 24, 2020 at 10:16

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.