Android 8.0: java.lang.IllegalStateException: Not allowed to start service Intent - android

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"

I got solution. For pre-8.0 devices, you have to just use startService(), but for post-7.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);

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.

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());

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

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.

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()
}
}

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.

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.

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.

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:
Above mentioned intent should be using JobIntentService instead of
IntentService.
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);
}
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) {
...
...
}
}
com.android.support:support-compat is needed for JobIntentService - I use 26.1.0 V.
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!
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.

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 );

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

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'

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

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.

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

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)
}
}

i had this problem too
added this library
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
and reinstalled the app solved this for me

Related

App keep Crashing on Oreo : android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground() [duplicate]

I am using Service Class on the Android O OS.
I plan to use the Service in the background.
The Android documentation states that
If your app targets API level 26 or higher, the system imposes restrictions on using or creating background services unless the app itself is in the foreground. If an app needs to create a foreground service, the app should call startForegroundService().
If you use startForegroundService(), the Service throws the following error.
Context.startForegroundService() did not then call
Service.startForeground()
What's wrong with this?
From Google's docs on Android 8.0 behavior changes:
The system allows apps to call Context.startForegroundService() even while the app is in the background. However, the app must call that service's startForeground() method within five seconds after the service is created.
Solution:
Call startForeground() in onCreate() for the Service which you use Context.startForegroundService()
See also: Background Execution Limits for Android 8.0 (Oreo)
I called ContextCompat.startForegroundService(this, intent) to start the service then
In service onCreate
#Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= 26) {
String CHANNEL_ID = "my_channel_01";
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT);
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("")
.setContentText("").build();
startForeground(1, notification);
}
}
Why this issue is happening is because Android framework can't guarantee your service get started within 5 second but on the other hand framework does have strict limit on foreground notification must be fired within 5 seconds, without checking if framework had tried to start the service.
This is definitely a framework issue, but not all developers facing this issue are doing their best:
startForeground a notification must be in both onCreate and onStartCommand, because if your service is already created and somehow your activity is trying to start it again, onCreate won't be called.
notification ID must not be 0 otherwise same crash will happen even it's not same reason.
stopSelf must not be called before startForeground.
With all above 3 this issue can be reduced a bit but still not a fix, the real fix or let's say workaround is to downgrade your target sdk version to 25.
And note that most likely Android P will still carry this issue because Google refuses to even understand what is going on and does not believe this is their fault, read #36 and #56 for more information
I know, too many answers have been published already, however the truth is - startForegroundService can not be fixed at an app level and you should stop using it. That Google recommendation to use Service#startForeground() API within 5 seconds after Context#startForegroundService() was called is not something that an app can always do.
Android runs a lot of processes simultaneously and there is no any guarantee that Looper will call your target service that is supposed to call startForeground() within 5 seconds. If your target service didn't receive the call within 5 seconds, you're out of luck and your users will experience ANR situation. In your stack trace you'll see something like this:
Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{1946947 u0 ...MessageService}
main" prio=5 tid=1 Native
| group="main" sCount=1 dsCount=0 flags=1 obj=0x763e01d8 self=0x7d77814c00
| sysTid=11171 nice=-10 cgrp=default sched=0/0 handle=0x7dfe411560
| state=S schedstat=( 1337466614 103021380 2047 ) utm=106 stm=27 core=0 HZ=100
| stack=0x7fd522f000-0x7fd5231000 stackSize=8MB
| held mutexes=
#00 pc 00000000000712e0 /system/lib64/libc.so (__epoll_pwait+8)
#01 pc 00000000000141c0 /system/lib64/libutils.so (android::Looper::pollInner(int)+144)
#02 pc 000000000001408c /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+60)
#03 pc 000000000012c0d4 /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44)
at android.os.MessageQueue.nativePollOnce (MessageQueue.java)
at android.os.MessageQueue.next (MessageQueue.java:326)
at android.os.Looper.loop (Looper.java:181)
at android.app.ActivityThread.main (ActivityThread.java:6981)
at java.lang.reflect.Method.invoke (Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1445)
As I understand, Looper has analyzed the queue here, found an "abuser" and simply killed it. The system is happy and healthy now, while developers and users are not, but since Google limits their responsibilities to the system, why should they care about the latter two? Apparently they don't. Could they make it better? Of course, e.g. they could've served "Application is busy" dialog, asking a user to make a decision about waiting or killing the app, but why bother, it's not their responsibility. The main thing is that the system is healthy now.
From my observations, this happens relatively rarely, in my case approximately 1 crash in a month for 1K users. Reproducing it is impossible, and even if it's reproduced, there is nothing you can do to fix it permanently.
There was a good suggestion in this thread to use "bind" instead of "start" and then when service is ready, process onServiceConnected, but again, it means not using startForegroundService calls at all.
I think, the right and honest action from Google side would be to tell everyone that startForegourndServcie has a deficiency and should not be used.
The question still remains: what to use instead? Fortunately for us, there are JobScheduler and JobService now, which are a better alternative for foreground services. It's a better option, because of that:
While a job is running, the system holds a wakelock on behalf of your
app. For this reason, you do not need to take any action to guarantee
that the device stays awake for the duration of the job.
It means that you don't need to care about handling wakelocks anymore and that's why it's not different from foreground services. From implementation point of view JobScheduler is not your service, it's a system's one, presumably it will handle the queue right, and Google will never terminate its own child :)
Samsung has switched from startForegroundService to JobScheduler and JobService in their Samsung Accessory Protocol (SAP). It's very helpful when devices like smartwatches need to talk to hosts like phones, where the job does need to interact with a user through an app's main thread. Since the jobs are posted by the scheduler to the main thread, it becomes possible. You should remember though that the job is running on the main thread and offload all heavy stuff to other threads and async tasks.
This service executes each incoming job on a Handler running on your
application's main thread. This means that you must offload your
execution logic to another thread/handler/AsyncTask of your choosing
The only pitfall of switching to JobScheduler/JobService is that you'll need to refactor old code, and it's not fun. I've spent last two days doing just that to use the new Samsung's SAP implementation. I'll watch my crash reports and let you know if see the crashes again. Theoretically it should not happen, but there are always details that we might not be aware of.
UPDATE
No more crashes reported by Play Store. It means that JobScheduler/JobService do not have such a problem and switching to this model is the right approach to get rid of startForegroundService issue once and forever. I hope, Google/Android reads it and will eventually comment/advise/provide an official guidance for everyone.
UPDATE 2
For those who use SAP and asking how SAP V2 utilizes JobService explanation is below.
In your custom code you'll need to initialize SAP (it's Kotlin) :
SAAgentV2.requestAgent(App.app?.applicationContext,
MessageJobs::class.java!!.getName(), mAgentCallback)
Now you need to decompile Samsung's code to see what's going on inside. In SAAgentV2 take a look at the requestAgent implementation and the following line:
SAAgentV2.d var3 = new SAAgentV2.d(var0, var1, var2);
where d defined as below
private SAAdapter d;
Go to SAAdapter class now and find onServiceConnectionRequested function that schedules a job using the following call:
SAJobService.scheduleSCJob(SAAdapter.this.d, var11, var14, var3, var12);
SAJobService is just an implementation of Android'd JobService and this is the one that does a job scheduling:
private static void a(Context var0, String var1, String var2, long var3, String var5, SAPeerAgent var6) {
ComponentName var7 = new ComponentName(var0, SAJobService.class);
Builder var10;
(var10 = new Builder(a++, var7)).setOverrideDeadline(3000L);
PersistableBundle var8;
(var8 = new PersistableBundle()).putString("action", var1);
var8.putString("agentImplclass", var2);
var8.putLong("transactionId", var3);
var8.putString("agentId", var5);
if (var6 == null) {
var8.putStringArray("peerAgent", (String[])null);
} else {
List var9;
String[] var11 = new String[(var9 = var6.d()).size()];
var11 = (String[])var9.toArray(var11);
var8.putStringArray("peerAgent", var11);
}
var10.setExtras(var8);
((JobScheduler)var0.getSystemService("jobscheduler")).schedule(var10.build());
}
As you see, the last line here uses Android'd JobScheduler to get this system service and to schedule a job.
In the requestAgent call we've passed mAgentCallback, which is a callback function that will receive control when an important event happens. This is how the callback is defined in my app:
private val mAgentCallback = object : SAAgentV2.RequestAgentCallback {
override fun onAgentAvailable(agent: SAAgentV2) {
mMessageService = agent as? MessageJobs
App.d(Accounts.TAG, "Agent " + agent)
}
override fun onError(errorCode: Int, message: String) {
App.d(Accounts.TAG, "Agent initialization error: $errorCode. ErrorMsg: $message")
}
}
MessageJobs here is a class that I've implemented to process all requests coming from a Samsung smartwatch. It's not the full code, only a skeleton:
class MessageJobs (context:Context) : SAAgentV2(SERVICETAG, context, MessageSocket::class.java) {
public fun release () {
}
override fun onServiceConnectionResponse(p0: SAPeerAgent?, p1: SASocket?, p2: Int) {
super.onServiceConnectionResponse(p0, p1, p2)
App.d(TAG, "conn resp " + p1?.javaClass?.name + p2)
}
override fun onAuthenticationResponse(p0: SAPeerAgent?, p1: SAAuthenticationToken?, p2: Int) {
super.onAuthenticationResponse(p0, p1, p2)
App.d(TAG, "Auth " + p1.toString())
}
override protected fun onServiceConnectionRequested(agent: SAPeerAgent) {
}
}
override fun onFindPeerAgentsResponse(peerAgents: Array<SAPeerAgent>?, result: Int) {
}
override fun onError(peerAgent: SAPeerAgent?, errorMessage: String?, errorCode: Int) {
super.onError(peerAgent, errorMessage, errorCode)
}
override fun onPeerAgentsUpdated(peerAgents: Array<SAPeerAgent>?, result: Int) {
}
}
As you see, MessageJobs requires MessageSocket class as well that you would need to implement and that processes all messages coming from your device.
Bottom line, it's not that simple and it requires some digging to internals and coding, but it works, and most importantly - it doesn't crash.
Your app will crash if you call Context.startForegroundService(...) and then call Context.stopService(...) before Service.startForeground(...) is called.
I have a clear repro here ForegroundServiceAPI26
I have opened a bug on this at : Google issue tracker
Several bugs on this have been opened and closed Won't Fix.
Hopefully mine with clear repro steps will make the cut.
Information provided by google team
Google issue tracker Comment 36
This is not a framework bug; it's intentional. If the app starts a service instance with startForegroundService(), it must transition that service instance to the foreground state and show the notification. If the service instance is stopped before startForeground() is called on it, that promise is unfulfilled: this is a bug in the app.
Re #31, publishing a Service that other apps can start directly is fundamentally unsafe. You can mitigate that a bit by treating all start actions of that service as requiring startForeground(), though obviously that may not be what you had in mind.
Google issue tracker Comment 56
There are a couple of different scenarios that lead to the same outcome here.
The outright semantic issue, that it's simply an error to kick something off with startForegroundService() but neglect to actually transition it to foreground via startForeground(), is just that: a semantic issue. That's treated as an app bug, intentionally. Stopping the service before transitioning it to foreground is an app error. That was the crux of the OP, and is why this issue has been marked "working as intended."
However, there are also questions about spurious detection of this problem. That's is being treated as a genuine problem, though it's being tracked separately from this particular bug tracker issue. We aren't deaf to the complaint.
Since everybody visiting here is suffering the same thing, I want to share my solution that nobody else has tried before (in this question anyways). I can assure you that it is working, even on a stopped breakpoint which confirms this method.
The issue is to call Service.startForeground(id, notification) from the service itself, right? Android Framework unfortunately does not guarantee to call Service.startForeground(id, notification) within Service.onCreate() in 5 seconds but throws the exception anyway, so I've come up with this way.
Bind the service to a context with a binder from the service before calling Context.startForegroundService()
If the bind is successful, call Context.startForegroundService() from the service connection and immediately call Service.startForeground() inside the service connection.
IMPORTANT NOTE: Call the Context.bindService() method inside a try-catch because in some occasions the call can throw an exception, in which case you need to rely on calling Context.startForegroundService() directly and hope it will not fail. An example can be a broadcast receiver context, however getting application context does not throw an exception in that case, but using the context directly does.
This even works when I'm waiting on a breakpoint after binding the service and before triggering the "startForeground" call. Waiting between 3-4 seconds do not trigger the exception while after 5 seconds it throws the exception. (If the device cannot execute two lines of code in 5 seconds, then it's time to throw that in the trash.)
So, start with creating a service connection.
// Create the service connection.
ServiceConnection connection = new ServiceConnection()
{
#Override
public void onServiceConnected(ComponentName name, IBinder service)
{
// The binder of the service that returns the instance that is created.
MyService.LocalBinder binder = (MyService.LocalBinder) service;
// The getter method to acquire the service.
MyService myService = binder.getService();
// getServiceIntent(context) returns the relative service intent
context.startForegroundService(getServiceIntent(context));
// This is the key: Without waiting Android Framework to call this method
// inside Service.onCreate(), immediately call here to post the notification.
myService.startForeground(myNotificationId, MyService.getNotification());
// Release the connection to prevent leaks.
context.unbindService(this);
}
#Override
public void onBindingDied(ComponentName name)
{
Log.w(TAG, "Binding has dead.");
}
#Override
public void onNullBinding(ComponentName name)
{
Log.w(TAG, "Bind was null.");
}
#Override
public void onServiceDisconnected(ComponentName name)
{
Log.w(TAG, "Service is disconnected..");
}
};
Inside your service, create a binder that returns the instance of your service.
public class MyService extends Service
{
public class LocalBinder extends Binder
{
public MyService getService()
{
return MyService.this;
}
}
// Create the instance on the service.
private final LocalBinder binder = new LocalBinder();
// Return this instance from onBind method.
// You may also return new LocalBinder() which is
// basically the same thing.
#Nullable
#Override
public IBinder onBind(Intent intent)
{
return binder;
}
}
Then, try to bind the service from that context. If it succeeds, it will call ServiceConnection.onServiceConnected() method from the service connection that you're using. Then, handle the logic in the code that's shown above. An example code would look like this:
// Try to bind the service
try
{
context.bindService(getServiceIntent(context), connection,
Context.BIND_AUTO_CREATE);
}
catch (RuntimeException ignored)
{
// This is probably a broadcast receiver context even though we are calling getApplicationContext().
// Just call startForegroundService instead since we cannot bind a service to a
// broadcast receiver context. The service also have to call startForeground in
// this case.
context.startForegroundService(getServiceIntent(context));
}
It seems to be working on the applications that I develop, so it should work when you try as well.
Now in Android O you can set the background limitation as below
The service which is calling a service class
Intent serviceIntent = new Intent(SettingActivity.this,DetectedService.class);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
SettingActivity.this.startForegroundService(serviceIntent);
} else {
startService(serviceIntent);
}
and the service class should be like
public class DetectedService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public void onCreate() {
super.onCreate();
int NOTIFICATION_ID = (int) (System.currentTimeMillis()%10000);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(NOTIFICATION_ID, new Notification.Builder(this).build());
}
// Do whatever you want to do here
}
}
I have a widget which does relatively frequent updates when the device is awake and I was seeing thousands of crashes in just a few days.
The issue trigger
I even noticed the issue even on my Pixel 3 XL when I wouldn't have thought the device to have much load at all. And any and all code paths were covered with startForeground(). But then I realized that in many cases my service gets the job done really quickly. I believe the trigger for my app was that the service was finishing before the system actually got around to showing a notification.
The workaround/solution
I was able to get rid of all crashes. What I did was to remove the call to stopSelf(). (I was thinking about delaying the stop until I was pretty sure the notification was shown, but I don't want the user to see the notification if it isn't necessary.) When the service has been idle for a minute or the system destroys it normally without throwing any exceptions.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
stopForeground(true);
} else {
stopSelf();
}
I have a work around for this problem. I have verified this fix in my own app(300K+ DAU), which can reduce at least 95% of this kind of crash, but still cannot 100% avoid this problem.
This problem happens even when you ensure to call startForeground() just after service started as Google documented. It may be because the service creation and initialization process already cost more than 5 seconds in many scenarios, then no matter when and where you call startForeground() method, this crash is unavoidable.
My solution is to ensure that startForeground() will be executed within 5 seconds after startForegroundService() method, no matter how long your service need to be created and initialized. Here is the detailed solution.
Do not use startForegroundService at the first place, use bindService() with auto_create flag. It will wait for the service initialization. Here is the code, my sample service is MusicService:
final Context applicationContext = context.getApplicationContext();
Intent intent = new Intent(context, MusicService.class);
applicationContext.bindService(intent, new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder binder) {
if (binder instanceof MusicBinder) {
MusicBinder musicBinder = (MusicBinder) binder;
MusicService service = musicBinder.getService();
if (service != null) {
// start a command such as music play or pause.
service.startCommand(command);
// force the service to run in foreground here.
// the service is already initialized when bind and auto_create.
service.forceForeground();
}
}
applicationContext.unbindService(this);
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
}, Context.BIND_AUTO_CREATE);
Then here is MusicBinder implementation:
/**
* Use weak reference to avoid binder service leak.
*/
public class MusicBinder extends Binder {
private WeakReference<MusicService> weakService;
/**
* Inject service instance to weak reference.
*/
public void onBind(MusicService service) {
this.weakService = new WeakReference<>(service);
}
public MusicService getService() {
return weakService == null ? null : weakService.get();
}
}
The most important part, MusicService implementation, forceForeground() method will ensure that startForeground() method is called just after service start:
public class MusicService extends MediaBrowserServiceCompat {
...
private final MusicBinder musicBind = new MusicBinder();
...
#Override
public IBinder onBind(Intent intent) {
musicBind.onBind(this);
return musicBind;
}
...
public void forceForeground() {
// API lower than 26 do not need this work around.
if (Build.VERSION.SDK_INT >= 26) {
Notification notification = mNotificationHandler.createNotification(this);
// call startForeground just after service start.
startForeground(Constants.NOTIFICATION_ID, notification);
}
}
}
If you want to run the step 1 code snippet in a pending intent, such as if you want to start a foreground service in a widget (a click on widget button) without opening your app, you can wrap the code snippet in a broadcast receiver, and fire a broadcast event instead of start service command.
That is all. Hope it helps. Good luck.
Just a heads up as I wasted way too many hours on this. I kept getting this exception even though I was calling startForeground(..) as the first thing in onCreate(..).
In the end I found that the problem was caused by using NOTIFICATION_ID = 0. Using any other value seems to fix this.
You have to add a permission as bellow for android 9 device when use target sdk 28 or later or the exception will always happen:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
This error also occurs on Android 8+ when Service.startForeground(int id, Notification notification) is called while id is set to 0.
id int: The identifier for this notification as per NotificationManager.notify(int, Notification); must not be 0.
So many answer but none worked in my case.
I have started service like this.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent);
} else {
startService(intent);
}
And in my service in onStartCommand
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
.setContentTitle(getString(R.string.app_name))
.setContentText("SmartTracker Running")
.setAutoCancel(true);
Notification notification = builder.build();
startForeground(NOTIFICATION_ID, notification);
} else {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setContentTitle(getString(R.string.app_name))
.setContentText("SmartTracker is Running...")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true);
Notification notification = builder.build();
startForeground(NOTIFICATION_ID, notification);
}
And don't forgot to set NOTIFICATION_ID non zero
private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
private static final int NOTIFICATION_ID = 555;
SO everything was perfect but still crashing on 8.1 so cause was as below.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
stopForeground(true);
} else {
stopForeground(true);
}
I have called stop foreground with remove notificaton but once notification removed service become background and background service can not run in android O from background. started after push received.
So magical word is
stopSelf();
So far so any reason your service is crashing follow all above steps and enjoy.
Please don't call any StartForgroundServices inside onCreate() method, you have to call StartForground services in onStartCommand() after make the worker thread otherwise you will get ANR always , so please don't write complex login in main thread of onStartCommand();
public class Services extends Service {
private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
.setContentTitle(getString(R.string.app_name))
.setContentText("SmartTracker Running")
.setAutoCancel(true);
Notification notification = builder.build();
startForeground(1, notification);
Log.e("home_button","home button");
} else {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setContentTitle(getString(R.string.app_name))
.setContentText("SmartTracker is Running...")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true);
Notification notification = builder.build();
startForeground(1, notification);
Log.e("home_button_value","home_button_value");
}
return super.onStartCommand(intent, flags, startId);
}
}
EDIT: Caution! startForeground function can't take 0 as first argument, it will raise an exception! this example contains wrong function call, change 0 to your own const which couldnt be 0 or be greater than Max(Int32)
I've been researching this issue and this is what I've discovered so far. This crash could happen if we have code similar to this:
MyForegroundService.java
public class MyForegroundService extends Service {
#Override
public void onCreate() {
super.onCreate();
startForeground(...);
}
}
MainActivity.java
Intent serviceIntent = new Intent(this, MyForegroundService.class);
startForegroundService(serviceIntent);
...
stopService(serviceIntent);
The exception is thrown in the following block of the code:
ActiveServices.java
private final void bringDownServiceLocked(ServiceRecord r) {
...
if (r.fgRequired) {
Slog.w(TAG_SERVICE, "Bringing down service while still waiting for start foreground: "
+ r);
r.fgRequired = false;
r.fgWaiting = false;
mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
mAm.mHandler.removeMessages(
ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
if (r.app != null) {
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);
msg.obj = r.app;
msg.getData().putCharSequence(
ActivityManagerService.SERVICE_RECORD_KEY, r.toString());
mAm.mHandler.sendMessage(msg);
}
}
...
}
This method is executed before onCreate() of MyForegroundService because Android schedules the creation of the service on the main thread handler but bringDownServiceLocked is called on a BinderThread, wich is a race condition. It means that MyForegroundService didn't have a chance to call startForeground which will cause the crash.
To fix this we have to make sure that bringDownServiceLocked is not called before onCreate() of MyForegroundService.
public class MyForegroundService extends Service {
private static final String ACTION_STOP = "com.example.MyForegroundService.ACTION_STOP";
private final BroadcastReceiver stopReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
context.removeStickyBroadcast(intent);
stopForeground(true);
stopSelf();
}
};
#Override
public void onCreate() {
super.onCreate();
startForeground(...);
registerReceiver(
stopReceiver, new IntentFilter(ACTION_STOP));
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(stopReceiver);
}
public static void stop(Context context) {
context.sendStickyBroadcast(new Intent(ACTION_STOP));
}
}
By using sticky broadcasts we make sure that the broadcast doesn't get lost and stopReceiver receives the stop intent as soon as it has been registered in onCreate() of MyForegroundService. By this time we have already called startForeground(...). We also have to remove that sticky broadcast to prevent stopReceiver being notified next time.
Please note that the method sendStickyBroadcast is deprecated and I use it only as a temporary workaround to fix this issue.
Around 10 users is getting this error in crashlytics for our application.
As Kimi Chiu replied- The main cause of this problem is the service was stopped before it was promoted to the foreground. But the assertion didn't stop after the service get destroyed. You can try to reproduce this by adding StopService after calling startForegroundService-Kimi Chiu
So I tested this and was able to reproduce.
One solution I applied is, I am letting the service to stay for at least 5 seconds so that the service will promote to the foreground. And now I cannot reproduce the issue while testing.
private fun stopService() {
lifecycleScope.launch {
delay(5000L)
try {
stopForeground(true)
isForeGroundService = false
stopSelf()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
Lets see if the issue is reproduce in our next build.
Update :)-> This time there was no issue related to Context.startForegroundService() did not then call Service.startForeground()
Before/After comparission->
Before->
After->
From Google's docs on Android 12 behavior changes:
To provide a streamlined experience for short-running foreground services on Android 12, the system can delay the display of foreground service notifications by 10 seconds for certain foreground services. This change gives short-lived tasks a chance to complete before their notifications appear.
Solution: Call startForeground() in onCreate() for the Service which you use Context.startForegroundService()
https://developer.android.com/reference/android/content/Context.html#startForegroundService(android.content.Intent)
Similar to startService(Intent), but with an implicit promise that the
Service will call startForeground(int, android.app.Notification) once
it begins running. The service is given an amount of time comparable
to the ANR interval to do this, otherwise the system will
automatically stop the service and declare the app ANR.
Unlike the ordinary startService(Intent), this method can be used at
any time, regardless of whether the app hosting the service is in a
foreground state.
make sure you call the Service.startForeground(int, android.app.Notification) on the onCreate() so you ensure it will be called..if you have any condition that may prevent you from doing that, then you'd better off using the normal Context.startService(Intent) and call the Service.startForeground(int, android.app.Notification) yourself.
It seems that the Context.startForegroundService() adds a watchdog to make sure you called the Service.startForeground(int, android.app.Notification) before it was destroyed...
I am facing same issue and after spending time found a solutons you can try below code. If your using Service then put this code in onCreate else your using Intent Service then put this code in onHandleIntent.
if (Build.VERSION.SDK_INT >= 26) {
String CHANNEL_ID = "my_app";
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
"MyApp", NotificationManager.IMPORTANCE_DEFAULT);
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("")
.setContentText("").build();
startForeground(1, notification);
}
Problem With Android O API 26
If you stop the service right away (so your service does not actually really runs (wording / comprehension) and you are way under the ANR interval, you still need to call startForeground before stopSelf
https://plus.google.com/116630648530850689477/posts/L2rn4T6SAJ5
Tried this Approach But it Still creates an error:-
if (Util.SDK_INT > 26) {
mContext.startForegroundService(playIntent);
} else {
mContext.startService(playIntent);
}
I Am Using this until the Error is Resolved
mContext.startService(playIntent);
Even after calling the startForeground in Service, It crashes on some devices if we call stopService just before onCreate is called.
So, I fixed this issue by Starting the service with an additional flag:
Intent intent = new Intent(context, YourService.class);
intent.putExtra("request_stop", true);
context.startService(intent);
and added a check in onStartCommand to see if it was started to stop:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//call startForeground first
if (intent != null) {
boolean stopService = intent.getBooleanExtra("request_stop", false);
if (stopService) {
stopSelf();
}
}
//Continue with the background task
return START_STICKY;
}
P.S. If the service were not running, it would start the service first, which is an overhead.
Updating Data in onStartCommand(...)
onBind(...)
onBind(...) is a better lifecycle event to initiate startForeground vs. onCreate(...) because onBind(...) passes in an Intent which may contain important data in the Bundle needed to initialize the Service. However, it is not necessary as onStartCommand(...) is called when the Service is created for the first time or called subsequent times after.
onStartCommand(...)
startForeground in onStartCommand(...) is important in order to update the Service once it has already been created.
When ContextCompat.startForegroundService(...) is called after a Service has been created onBind(...) and onCreate(...) are not called. Therefore, updated data can be passed into onStartCommand(...) via the Intent Bundle to update data in the Service.
Sample
I'm using this pattern to implement the PlayerNotificationManager in the Coinverse cryptocurrency news app.
Activity / Fragment.kt
context?.bindService(
Intent(context, AudioService::class.java),
serviceConnection, Context.BIND_AUTO_CREATE)
ContextCompat.startForegroundService(
context!!,
Intent(context, AudioService::class.java).apply {
action = CONTENT_SELECTED_ACTION
putExtra(CONTENT_SELECTED_KEY, contentToPlay.content.apply {
audioUrl = uri.toString()
})
})
AudioService.kt
private var uri: Uri = Uri.parse("")
override fun onBind(intent: Intent?) =
AudioServiceBinder().apply {
player = ExoPlayerFactory.newSimpleInstance(
applicationContext,
AudioOnlyRenderersFactory(applicationContext),
DefaultTrackSelector())
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
intent?.let {
when (intent.action) {
CONTENT_SELECTED_ACTION -> it.getParcelableExtra<Content>(CONTENT_SELECTED_KEY).also { content ->
val intentUri = Uri.parse(content.audioUrl)
// Checks whether to update Uri passed in Intent Bundle.
if (!intentUri.equals(uri)) {
uri = intentUri
player?.prepare(ProgressiveMediaSource.Factory(
DefaultDataSourceFactory(
this,
Util.getUserAgent(this, getString(app_name))))
.createMediaSource(uri))
player?.playWhenReady = true
// Calling 'startForeground' in 'buildNotification(...)'.
buildNotification(intent.getParcelableExtra(CONTENT_SELECTED_KEY))
}
}
}
}
return super.onStartCommand(intent, flags, startId)
}
// Calling 'startForeground' in 'onNotificationStarted(...)'.
private fun buildNotification(content: Content): Unit? {
playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
this,
content.title,
app_name,
if (!content.audioUrl.isNullOrEmpty()) 1 else -1,
object : PlayerNotificationManager.MediaDescriptionAdapter {
override fun createCurrentContentIntent(player: Player?) = ...
override fun getCurrentContentText(player: Player?) = ...
override fun getCurrentContentTitle(player: Player?) = ...
override fun getCurrentLargeIcon(player: Player?,
callback: PlayerNotificationManager.BitmapCallback?) = ...
},
object : PlayerNotificationManager.NotificationListener {
override fun onNotificationStarted(notificationId: Int, notification: Notification) {
startForeground(notificationId, notification)
}
override fun onNotificationCancelled(notificationId: Int) {
stopForeground(true)
stopSelf()
}
})
return playerNotificationManager.setPlayer(player)
}
Ok, something I noticed on this that might help a few others too. This is strictly from testing to see if I could figure out how to fix the occurrences I am seeing. For simplicity sake, let's say I have a method that calls this from the presenter.
context.startForegroundService(new Intent(context, TaskQueueExecutorService.class));
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
This will crash with the same error. The Service will NOT start until the method is complete, therefore no onCreate() in the service.
So even if you update the UI off the main thread, IF you have anything that might hold up that method after it, it won't start on time and give you the dreaded Foreground Error. In my case we were loading some things onto a queue and each called startForegroundService, but some logic was involved with each in the background. So if the logic took too long to finish that method since they were called back to back, crash time. The old startService just ignored it and went on it's way and since we called it each time, the next round would finish up.
This left me wondering, if I called the service from a thread in the background, could it not be fully bound on the start and run immediately, so I started experimenting. Even though this does NOT start it immediately, it does not crash.
new Handler(Looper.getMainLooper()).post(new Runnable() {
public void run() {
context.startForegroundService(new Intent(context,
TaskQueueExecutorService.class));
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
I will not pretend to know why it does not crash although I suspect this forces it to wait until the main thread can handle it in a timely fashion. I know it's not ideal to tie it to the main thread, but since my usage calls it in the background, I'm not real concerned if it waits until it can complete rather than crash.
I am adding some code in #humazed answer. So there in no initial notification. It might be a workaround but it works for me.
#Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= 26) {
String CHANNEL_ID = "my_channel_01";
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT);
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("")
.setContentText("")
.setColor(ContextCompat.getColor(this, R.color.transparentColor))
.setSmallIcon(ContextCompat.getColor(this, R.color.transparentColor)).build();
startForeground(1, notification);
}
}
I am adding transparentColor in small icon and color on notification.
It will work.
One issue might be Service class is not enabled in AndroidManifest file.
Please check it as well.
<service
android:name=".AudioRecorderService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="microphone" />
I had an issue in Pixel 3, Android 11 that when my service was running very short, then the foreground notification was not dismissed.
Adding 100ms delay before stopForeground() stopSelf() seems to help.
People write here that stopForeground() should be called before stopSelf(). I cannot confirm, but I guess it doesn't bother to do that.
public class AService extends Service {
#Override
public void onCreate() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(
getForegroundNotificationId(),
channelManager.buildBackgroundInfoNotification(getNotificationTitle(), getNotificationText()),
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC);
} else {
startForeground(getForegroundNotificationId(),
channelManager.buildBackgroundInfoNotification(getNotificationTitle(), getNotificationText())
);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground();
if (hasQueueMoreItems()) {
startWorkerThreads();
} else {
stopForeground(true);
stopSelf();
}
return START_STICKY;
}
private class WorkerRunnable implements Runnable {
#Override
public void run() {
while (getItem() != null && !isLoopInterrupted) {
doSomething(getItem())
}
waitALittle();
stopForeground(true);
stopSelf();
}
private void waitALittle() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
I just check the PendingIntent null or nor not before calling the
context.startForegroundService(service_intent) function.
this works for me
PendingIntent pendingIntent=PendingIntent.getBroadcast(context,0,intent,PendingIntent.FLAG_NO_CREATE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && pendingIntent==null){
context.startForegroundService(service_intent);
}
else
{
context.startService(service_intent);
}
}
just call startForeground method immediately after Service or IntentService is Created. like this:
import android.app.Notification;
public class AuthenticationService extends Service {
#Override
public void onCreate() {
super.onCreate();
startForeground(1,new Notification());
}
}
I have fixed the problem with starting the service with startService(intent) instead of Context.startForeground() and calling startForegound() immediately after super.OnCreate(). Additionally, if you starting service on boot, you can start Activity that starts service on the boot broadcast. Although it is not a permanent solution, it works.
I just sharing my review about this. I am not surely(100% telling) that above code is not working for me and other guys also but some times I got this issue. Suppose I run the app 10 time then might be got this issue 2 to 3 three time.
I have tried above all the answers but still not solve the issue. I have implemented above all the codes and tested in different api levels (API level 26, 28, 29) and difference mobile (Samsung, Xiaomi, MIUI, Vivo, Moto, One Plus, Huawei, etc ) and getting same below issue.
Context.startForegroundService() did not then call Service.startForeground();
I have read service on google developer web site, some other blog and some stack overflow question and got the idea that this issue will happen when we call startForgroundSerivce() method but at that time service was not started.
In my case I have stop the service and after immediately start service. Below is the hint.
....//some other code
...// API level and other device auto star service condition is already set
stopService();
startService();
.....//some other code
In this case service is not started due to processing speed and low memory in RAM but startForegroundService() method is called and fire the exception.
Work for me:
new Handler().postDelayed(()->ContextCompat.startForegroundService(activity, new Intent(activity, ChatService.class)), 500);
I have change code and set 500 milliseconds delay to call startService() method and issue is solved. This is not perfect solution because this way app's performance goes downgrade.
Note:
This is only for Foreground and Background service only. Don't tested when using Bind service.
I am sharing this because only this is the way I have solved this issue.

Android Oreo JobIntentService Keep running in background for Android 7 &below and crashing often in Android 8 & above

I have recently replaced all my service to foreground services and JobIntentService since there are some background execution limits (https://developer.android.com/about/versions/oreo/background) in oreo and above. As per documentation, JobIntentService acts like Intent Service for Android 7 & below and acts like JobScheduler for Android 8 & above. I have noticed there is an issue in new JobIntentService provided by Google.
Android 8 & above:
There is a crash happening continuously in android 8 and above. There was a ticket raised here mentioning about the same issue https://issuetracker.google.com/issues/63622293 and I have added a temp fix suggested by few geeks.
Android 7 & below:
JobIntentService which acts like Intent Service is not getting stopped once the work is done.
I have implemented JobIntentService within a service which triggers whenever some action is performed by a user.
Code
public class SampleJobIntentService extends FixedJobIntentService {
public static void postData(Context context, String data) {
Intent intent = new Intent(context, SampleJobIntentService.class);
intent.setAction(INITIAL_ACTION);
intent.putExtra(SAMPLE_ID, data);
SampleJobIntentService.enqueueWork(context,intent);
}
public static void enqueueWork(Context context, Intent work) {
SampleJobIntentService.enqueueWork(context, SampleJobIntentService.class, JOB_ID, work);
#Override
protected void onHandleWork(#NonNull Intent intent) {
if (intent != null) {
SampleRequest sampleRequest = requests.get(intent.getAction());
if (sampleRequest != null) {
try {
// perform some networking operations
} catch (Exception ex) {
Log.d("Error for intent ");
}
Log.i("send action ");
} else
Log.e("action not found for ");
}
}
}
To avoid the crash with JobIntentService, I took few references from https://issuetracker.google.com/issues/63622293
public abstract class FixedJobIntentService extends JobIntentService {
#Override
GenericWorkItem dequeueWork() {
try {
return new FixedGenericWorkItem(super.dequeueWork());
} catch (SecurityException ignored) {
doStopCurrentWork();
}
return null;
}
private class FixedGenericWorkItem implements GenericWorkItem {
final GenericWorkItem mGenericWorkItem;
FixedGenericWorkItem(GenericWorkItem genericWorkItem) {
mGenericWorkItem = genericWorkItem;
}
#Override
public Intent getIntent() {
if (mGenericWorkItem != null) {
return mGenericWorkItem.getIntent();
}
return null;
}
#Override
public void complete() {
try {
if (mGenericWorkItem != null) {
mGenericWorkItem.complete();
}
} catch (IllegalArgumentException ignored) {
doStopCurrentWork();
}
}
}
}
Well..., Its a lot big theory...!! It would not be able to put it all here. I will try my best which will make some your concepts clear.
I have already lost my 2 complete years in reading google documentations... Which are use-less... With no proper documentation and with no proper sample codes for its developers..!! So i mention this in every of my posts on stack-overflow, As it will help to save time of others..!!
It looks you are a good programmer; just need some hints to your posted question :
Hint-1 :
YOU :- I have recently replaced all my service to foreground services and
JobIntentService
foreground service :
If you need ALL THE TIME RUNNING PROCESS; WHICH WILL NEVER END... ONCE IT IS STARTED it is used in service which returns START_STICKY from its OnStartCommand. Which is again not advised to use as if you want to implement it at any cost ... then you will have to use a notification with setOngoing(true) Which end user would not be able to swipe away your notification, it will remain there forever....
Use of the foreground service :
There has been restrictions on receivers too; above Oreo onwards and you can not use all the receivers and intent actions by declaring it in manifest and by just making a receiver... I advice to just use BootComplete permission and use a single receiver which receives the boot_completed intent and calls a service if below O and calls a foreground service above O. Now from that foreground service you implement the runtime receivers for all and unregister it in Ondestroy methods. I have never found an official sample code for implementing runtime receiver and finally i have implemented it successfully by many months hard-work... Yes it was not a smart work due to google
When to use foreground service :
Only if you want to implement broadcast receivers.... If you do not want to implement any broadcast receivers; STAY AWAY.......
Hint-2 :
YOU :- I have recently replaced all my service to foreground services and
JobIntentService
** service has its quality of :**
Just doing a very tiny work... and just exit... it has to be exited by StopSelf()... Again, Services can cause data-loss if called multiple times... As same service thread can be run more than once... Again if you want a service to do a lot of work... Use START_STICKY... But again it is not recommended and i have suggested already, when to use it in Hint 1.
** Intentservice has its quality of :**
Doing a relatively long running tasks and it has property of execution serially only If you again and again calls the same intentService, then all calls will be kept in a queue and will be executed one by one after finishing one by one. Which is not the case in service as depicted above. It ends on its own... no need to end it by a developer..!!
** Unique Quality of all :**
Once they are crashed android can stop them calling in future without notifying you as it crashes the app. Need to be handled them with try-catch-exception to avoid crash. Again... If you are implementing threads within services then try-catch-exception will not save your application from being crashing...
** THEN WHAT THE HELL & HOW TO IMPLEMENT IT THEN :**
Use FireBaseJobScedular :-
Easy to use
Uses simple JobService
Can run longer or smaller time tasks... EVEN ALL THE TIME RUNNING TASK
EVEN SUPPORTED BY NON STANDARD COMPANIES like vivo, mi, oppo, one+3, ... which takes stock-android makes changes to it and gives names like FunTouchOs, ColorOs, OxygenOs
Just need to Do change battery settings to "Do not optimise this app"
Yes google supports it officially and recommends to use it
It Creates the instance of GooglePlyService and runs within it, And obviously non-standards companies too would not restrict google apps from being doing its tasks.
Works on Oreo .., Even i have tested it on Android P and works below Android version 5.0 as AlarmManager tasks.
Still i recommends to use minsdk above 16, target sdk 26 as if in case you wants to upload your app to google play it is compulsory now and that news would have been heard you. and compile sdk 26.
Just Bind Your JobService in manifest and use a single line permission of receive_boot_complete
Just schedule it ... And it will be started on every device in market from every manufacturer... even on cold boot and hot boot
It minimises a lot, lot and lot of code and you can focus on actual tasks.
Once task is finished you can return false to indicate task has been finished and it will end the JobService.
Why i am suggesting because i am CTO of a well-UNKNOwn company and has been experienced the problems caused by foreground service across the many types of android phone manufacturers... It is not the Apple and ios so we had to experienced it. Remain developer since past 18 years and i mostly codes today too... in all of the development projects and its development strategies are thought by me only.
Correct me ... too... As you have not mentioned what your tasks and
project is related to... and what you exactly wants to be done in a
foreground service and intent-service... Let me know..., It would be my pleasure to help you. It is a general theoretical answer rather than what you wants.... But for giving you actual answer i will need exact your project scope..
JobIntentService which acts like Intent Service is not getting stopped once the work is done
The issue is in your extended class FixedJobIntentService dequeueWork method.
Try changing it to something like below
GenericWorkItem superValue = super.dequeueWork();
if (superValue != null) {
return new FixedGenericWorkItem(superValue);
}
return null;
Looking at the JobIntentSerivce code, Work Items processor logic is below, i.e until there are no work items left in the queue all items are processed (i.e onHandleWork is called for each item)
while ((work = dequeueWork()) != null) {
if (DEBUG) Log.d(TAG, "Processing next work: " + work);
onHandleWork(work.getIntent());
if (DEBUG) Log.d(TAG, "Completing work: " + work);
work.complete();
}
Issue in your implementation is after processing the first work item, the super.dequeueWork() returns null, which you are not taking care of and just sending a new FixedGenericWorkItem object passing null value. You might observe that a null value is passed to your onHandleWork in your subsequent calls.
Hope this helps resolve your issue.
I think you just need this much of a code. Create a new Class MyJobIntentService and write this much of a code and call postData() to start your service.
public class MyJobIntentService extends JobIntentService {
public static void postData(Context context, String data) {
final Intent intent = new Intent(context, MyJobIntentService.class);
intent.setAction(INITIAL_ACTION);
intent.putExtra(SAMPLE_ID, data);
enqueueWork(context, MyJobIntentService.class, 1000, intent);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
Ln.d("Cancelled service");
super.onDestroy();
}
#Override
protected void onHandleWork(#NonNull Intent intent) {
if (intent != null) {
final SampleRequest sampleRequest = requests.get(intent.getAction());
if (sampleRequest != null) {
try {
// perform some networking operations
} catch (Exception ex) {
Log.d("Error for intent ");
}
Log.i("send action ");
} else {
Log.e("action not found for ");
}
}
}
}
And make sure to add your service in manifest file
<service
android:name="service.MyJobIntentService"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE" />

Continually Running Background Service

I'm targeting sdk version 27 with a minimum version of 19 and trying to get a service that runs continuously in the background. I tried different service start options but it still got killed with the app. I tried using a BroadcastReceiver to start the service when it got killed but that gave me an error saying that the app was in the background and couldn't start a service so I tried using the JobScheduler and that gave me the same error. How is this supposed to be done? For example, if I were making a pedometer app, how could I keep that running in the background?
In oreo release Android defined limits to background services.
To improve the user experience, Android 8.0 (API level 26) imposes
limitations on what apps can do while running in the background.
Still if app need to run its service always, then we can create foreground service.
Background Service Limitations: While an app is idle, there are limits
to its use of background services. This does not apply to foreground
services, which are more noticeable to the user.
So create a foreground service. In which you will put a notification for user while your service is running. See this answer (There are many others)
Now what if you don't want a notification for your service. A solution is for that.
You can create some periodic task that will start your service, service will do its work and stops itself. By this your app will not be considered battery draining.
You can create periodic task with Alarm Manager, Job Scheduler, Evernote-Jobs or Work Manager.
Instead of telling pros & cons of each one. I just tell you best. Work manager is best solution for periodic tasks. Which was introduced with Android Architecture Component.
Unlike Job-Scheduler(only >21 API) it will work for all versions.
Also it starts work after a Doze-Standby mode.
Make a Android Boot Receiver for scheduling service after device boot.
I created forever running service with Work-Manager, that is working perfectly.
Since Android 8.0 many background service limitations have been introduced.
Two solutions:
if you need to get total control of task and execution timing, you have to choose Foreground Service.
Pros: your app will be considered to be alive, then is more unlikely that the os will kill it to free resources.
Cons: your user will always see the Foreground Notification.
if you need to schedule periodically task, then Work Manager (introduced in Google I/O 18) is the best solution. This component choose the best possible scheduler (Jobscheduler, JobDispatcher, AlarmManager..). Keep in mind that work manager APIs are useful only for the tasks that require guaranteed execution and they are deferrable.
Ref: Android Dev Documentation
The only solution I would suggest is using Firebase Cloud Messages.
Or foreground services.
Using BroadcastReciever we can run backgrouund service continuously, but if it will get killed , destroy automatically re-instance the old service instance
When service stops forcefully it will call onDestroy() method, in this case use one receiver and send one broadcast when ever service destroy and restart service again. in thee following method com.android.app is custom action of reciever class which extends BroadcastReciever
public void onDestroy() {
try {
myTimer.cancel();
timerTask.cancel();
} catch (Exception e) {
e.printStackTrace();
}
Intent intent = new Intent("com.android.app");
intent.putExtra("valueone", "tostoreagain");
sendBroadcast(intent);
}
and in onReceive Method
#Override
public void onReceive(Context context, Intent intent) {
Log.i("Service Stoped", "call service again");
context.startService(new Intent(context, ServiceCheckWork.class));
}
In case device is restarted then we have onBootCompleted action for receiver to catch
When you are targeting SdkVersion "O"
In MainActivity.java define getPendingIntent()
private PendingIntent getPendingIntent() {
Intent intent = new Intent(this, YourBroadcastReceiver.class);
intent.setAction(YourBroadcastReceiver.ACTION_PROCESS_UPDATES);
return PendingIntent.getBroadcast(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
here we use PendingIntent with BroadcastReceiver and This BroadcastReceiver has already been defined in AndroidManifest.xml.
Now in YourBroadcastReceiver.java class which contains an onReceive() method:
Override
public void onReceive(Context context, Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_PROCESS_UPDATES.equals(action)) {
NotificationResult result = NotificationResult.extractResult(intent);
if (result != null) {
List<Notification> notifications = result.getNotification();
NotificationResultHelper notificationResultHelper = new
NotificationResultHelper(
context, notifications);
// Save the notification data to SharedPreferences.
notificationResultHelper.saveResults();
// Show notification with the notification data.
notificationResultHelper.showNotification();
Log.i(TAG,
NotificationResultHelper.getSavedNotificationResult(context));
}
}
}
}
as you say:
I tried using a BroadcastReceiver to start the service when it got
killed but that gave me an error saying that the app was in the
background and couldn't start a service
in Oreo when you are in background and you want to start a service that service must be a foreground service use this code:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
if you use this code in Oreo you have a few seconds in onStartCommand to start foreground otherwise your service considered as not responding and may be force close by user (in Android 8 or above)
There is no need to use BroadcastReceiver to start service after it is closed it is enough to just return START_STICKY or START_REDELIVER_INTENT from onStartCommand of your service to restart service after it is closed
A working hack for this is to simply start a foreground service which is only visible for the fraction of a second and starts your background service. In the background service you'd then periodically start the foreground service.
Before I give an example you should really ask yourself if this is the way to go for you, there might be other solutions to given problems (like using JobIntentService etc.); and keep in mind that this is a hack, it might be patched some time around and I'd generally not use it (I tested it with screen off and battery saving enabled though and it stayed alive the whole time - but this might prevent your device from dozing.. again, this is a dirty hack!)
Example:
public class TemporaryForegroundService extends Service {
public static final int NOTIFICATION_ID = 666;
private static Notification notification;
#Override
public void onCreate() {
super.onCreate();
if(notification == null)
notification = new NotificationCompat.Builder(this, NotificationChannels.importantChannel(this)).
setSmallIcon(R.mipmap.ic_launcher).setContentTitle("The unseen blade").setContentText("If you see me, congrats to you.").build();
startForeground(NOTIFICATION_ID, notification);
startService(new Intent(this, PermanentBackgroundService.class));
stopForeground(true);
stopSelf();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
public class PermanentBackgroundService extends Service {
private Runnable keepAliveRunnable = new Runnable() {
#Override
public void run() {
keepServiceAlive();
if(handler != null) handler.postDelayed(this, 15*1000);
}
};
private Handler handler;
public void onCreate(){
handler = new Handler();
handler.postDelayed(keepAliveRunnable, 30* 1000);
}
public void onDestroy() {
super.onDestroy();
keepServiceAlive();
}
private void keepServiceAlive() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(new Intent(PermanentBackgroundService.this, TemporaryForegroundService .class));
} else {
startService(new Intent(PermanentBackgroundService.this, TemporaryForegroundService .class));
}
}
}

Start service with JobScheduler on Android O

I'm trying to start a IntentService to register to a firebase cloud messaging on Android O.
On Android O it's not allowed to start a Intent Service "in a situation when it isn't permitted" and every one tells me to use a JobService but not how to use it.
What constraints should the JobInfo.Builder have in order to have a "situation where it's permitted", i keep getting the same IllegalStateException
Here's my JobService
#Override
public boolean onStartJob(JobParameters params) {
Intent intent = new Intent(this, RegistrationIntentService.class);
getApplicationContext().startService(intent);
return false;
}
#Override
public boolean onStopJob(JobParameters params) {
return false;
}
public static void scheduleJob(Context context) {
ComponentName serviceComponent = new ComponentName(context, MyJobService.class);
JobInfo.Builder builder = new JobInfo.Builder(MyJobService.JOB_ID, serviceComponent);
builder.setMinimumLatency(1 * 1000); // wait at least
JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
if(jobScheduler != null) jobScheduler.schedule(builder.build());
}
If you are using support library version 26.1.0 or higher you have access to the JobIntentService which is similar to an Intent Service with the added benefits of the job scheduler, you do not need to manage anything other than starting it.
According to the docs
Helper for processing work that has been enqueued for a job/service. When running on Android O or later, the work will be dispatched as a job via JobScheduler.enqueue. When running on older versions of the platform, it will use Context.startService.
You can find out more details here JobIntentService.
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v4.app.JobIntentService;
public class JobIntentNotificationService extends JobIntentService {
public static void start(Context context) {
Intent starter = new Intent(context, JobIntentNotificationService.class);
JobIntentNotificationService.enqueueWork(context, starter);
}
/**
* Unique job ID for this service.
*/
static final int JOB_ID = 1000;
/**
* Convenience method for enqueuing work in to this service.
*/
private static void enqueueWork(Context context, Intent intent) {
enqueueWork(context, JobIntentNotificationService.class, JOB_ID, intent);
}
#Override
protected void onHandleWork(#NonNull Intent intent) {
// do your work here
}
}
And the way you call it is
JobIntentNotificationService.start(getApplicationContext());
You will need to add this permission for pre Oreo devices
<!-- used for job scheduler pre Oreo -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
Firebase actually has a dedicated service for receiving messages called FirebaseMessagingService. This Firebase page should contain all the info to get you started in that regard.
Aside from that, you're trying to access the application context from the Service, while you should be using the base context of the parent service:
getApplicationContext().startService(intent);
to
startService(intent);
If you want to launch certain jobs from the FirebaseMessagingService, look into their JobDispatcher library which is pretty great.

Context.startForegroundService() did not then call Service.startForeground()

I am using Service Class on the Android O OS.
I plan to use the Service in the background.
The Android documentation states that
If your app targets API level 26 or higher, the system imposes restrictions on using or creating background services unless the app itself is in the foreground. If an app needs to create a foreground service, the app should call startForegroundService().
If you use startForegroundService(), the Service throws the following error.
Context.startForegroundService() did not then call
Service.startForeground()
What's wrong with this?
From Google's docs on Android 8.0 behavior changes:
The system allows apps to call Context.startForegroundService() even while the app is in the background. However, the app must call that service's startForeground() method within five seconds after the service is created.
Solution:
Call startForeground() in onCreate() for the Service which you use Context.startForegroundService()
See also: Background Execution Limits for Android 8.0 (Oreo)
I called ContextCompat.startForegroundService(this, intent) to start the service then
In service onCreate
#Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= 26) {
String CHANNEL_ID = "my_channel_01";
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT);
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("")
.setContentText("").build();
startForeground(1, notification);
}
}
Why this issue is happening is because Android framework can't guarantee your service get started within 5 second but on the other hand framework does have strict limit on foreground notification must be fired within 5 seconds, without checking if framework had tried to start the service.
This is definitely a framework issue, but not all developers facing this issue are doing their best:
startForeground a notification must be in both onCreate and onStartCommand, because if your service is already created and somehow your activity is trying to start it again, onCreate won't be called.
notification ID must not be 0 otherwise same crash will happen even it's not same reason.
stopSelf must not be called before startForeground.
With all above 3 this issue can be reduced a bit but still not a fix, the real fix or let's say workaround is to downgrade your target sdk version to 25.
And note that most likely Android P will still carry this issue because Google refuses to even understand what is going on and does not believe this is their fault, read #36 and #56 for more information
I know, too many answers have been published already, however the truth is - startForegroundService can not be fixed at an app level and you should stop using it. That Google recommendation to use Service#startForeground() API within 5 seconds after Context#startForegroundService() was called is not something that an app can always do.
Android runs a lot of processes simultaneously and there is no any guarantee that Looper will call your target service that is supposed to call startForeground() within 5 seconds. If your target service didn't receive the call within 5 seconds, you're out of luck and your users will experience ANR situation. In your stack trace you'll see something like this:
Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{1946947 u0 ...MessageService}
main" prio=5 tid=1 Native
| group="main" sCount=1 dsCount=0 flags=1 obj=0x763e01d8 self=0x7d77814c00
| sysTid=11171 nice=-10 cgrp=default sched=0/0 handle=0x7dfe411560
| state=S schedstat=( 1337466614 103021380 2047 ) utm=106 stm=27 core=0 HZ=100
| stack=0x7fd522f000-0x7fd5231000 stackSize=8MB
| held mutexes=
#00 pc 00000000000712e0 /system/lib64/libc.so (__epoll_pwait+8)
#01 pc 00000000000141c0 /system/lib64/libutils.so (android::Looper::pollInner(int)+144)
#02 pc 000000000001408c /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+60)
#03 pc 000000000012c0d4 /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44)
at android.os.MessageQueue.nativePollOnce (MessageQueue.java)
at android.os.MessageQueue.next (MessageQueue.java:326)
at android.os.Looper.loop (Looper.java:181)
at android.app.ActivityThread.main (ActivityThread.java:6981)
at java.lang.reflect.Method.invoke (Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1445)
As I understand, Looper has analyzed the queue here, found an "abuser" and simply killed it. The system is happy and healthy now, while developers and users are not, but since Google limits their responsibilities to the system, why should they care about the latter two? Apparently they don't. Could they make it better? Of course, e.g. they could've served "Application is busy" dialog, asking a user to make a decision about waiting or killing the app, but why bother, it's not their responsibility. The main thing is that the system is healthy now.
From my observations, this happens relatively rarely, in my case approximately 1 crash in a month for 1K users. Reproducing it is impossible, and even if it's reproduced, there is nothing you can do to fix it permanently.
There was a good suggestion in this thread to use "bind" instead of "start" and then when service is ready, process onServiceConnected, but again, it means not using startForegroundService calls at all.
I think, the right and honest action from Google side would be to tell everyone that startForegourndServcie has a deficiency and should not be used.
The question still remains: what to use instead? Fortunately for us, there are JobScheduler and JobService now, which are a better alternative for foreground services. It's a better option, because of that:
While a job is running, the system holds a wakelock on behalf of your
app. For this reason, you do not need to take any action to guarantee
that the device stays awake for the duration of the job.
It means that you don't need to care about handling wakelocks anymore and that's why it's not different from foreground services. From implementation point of view JobScheduler is not your service, it's a system's one, presumably it will handle the queue right, and Google will never terminate its own child :)
Samsung has switched from startForegroundService to JobScheduler and JobService in their Samsung Accessory Protocol (SAP). It's very helpful when devices like smartwatches need to talk to hosts like phones, where the job does need to interact with a user through an app's main thread. Since the jobs are posted by the scheduler to the main thread, it becomes possible. You should remember though that the job is running on the main thread and offload all heavy stuff to other threads and async tasks.
This service executes each incoming job on a Handler running on your
application's main thread. This means that you must offload your
execution logic to another thread/handler/AsyncTask of your choosing
The only pitfall of switching to JobScheduler/JobService is that you'll need to refactor old code, and it's not fun. I've spent last two days doing just that to use the new Samsung's SAP implementation. I'll watch my crash reports and let you know if see the crashes again. Theoretically it should not happen, but there are always details that we might not be aware of.
UPDATE
No more crashes reported by Play Store. It means that JobScheduler/JobService do not have such a problem and switching to this model is the right approach to get rid of startForegroundService issue once and forever. I hope, Google/Android reads it and will eventually comment/advise/provide an official guidance for everyone.
UPDATE 2
For those who use SAP and asking how SAP V2 utilizes JobService explanation is below.
In your custom code you'll need to initialize SAP (it's Kotlin) :
SAAgentV2.requestAgent(App.app?.applicationContext,
MessageJobs::class.java!!.getName(), mAgentCallback)
Now you need to decompile Samsung's code to see what's going on inside. In SAAgentV2 take a look at the requestAgent implementation and the following line:
SAAgentV2.d var3 = new SAAgentV2.d(var0, var1, var2);
where d defined as below
private SAAdapter d;
Go to SAAdapter class now and find onServiceConnectionRequested function that schedules a job using the following call:
SAJobService.scheduleSCJob(SAAdapter.this.d, var11, var14, var3, var12);
SAJobService is just an implementation of Android'd JobService and this is the one that does a job scheduling:
private static void a(Context var0, String var1, String var2, long var3, String var5, SAPeerAgent var6) {
ComponentName var7 = new ComponentName(var0, SAJobService.class);
Builder var10;
(var10 = new Builder(a++, var7)).setOverrideDeadline(3000L);
PersistableBundle var8;
(var8 = new PersistableBundle()).putString("action", var1);
var8.putString("agentImplclass", var2);
var8.putLong("transactionId", var3);
var8.putString("agentId", var5);
if (var6 == null) {
var8.putStringArray("peerAgent", (String[])null);
} else {
List var9;
String[] var11 = new String[(var9 = var6.d()).size()];
var11 = (String[])var9.toArray(var11);
var8.putStringArray("peerAgent", var11);
}
var10.setExtras(var8);
((JobScheduler)var0.getSystemService("jobscheduler")).schedule(var10.build());
}
As you see, the last line here uses Android'd JobScheduler to get this system service and to schedule a job.
In the requestAgent call we've passed mAgentCallback, which is a callback function that will receive control when an important event happens. This is how the callback is defined in my app:
private val mAgentCallback = object : SAAgentV2.RequestAgentCallback {
override fun onAgentAvailable(agent: SAAgentV2) {
mMessageService = agent as? MessageJobs
App.d(Accounts.TAG, "Agent " + agent)
}
override fun onError(errorCode: Int, message: String) {
App.d(Accounts.TAG, "Agent initialization error: $errorCode. ErrorMsg: $message")
}
}
MessageJobs here is a class that I've implemented to process all requests coming from a Samsung smartwatch. It's not the full code, only a skeleton:
class MessageJobs (context:Context) : SAAgentV2(SERVICETAG, context, MessageSocket::class.java) {
public fun release () {
}
override fun onServiceConnectionResponse(p0: SAPeerAgent?, p1: SASocket?, p2: Int) {
super.onServiceConnectionResponse(p0, p1, p2)
App.d(TAG, "conn resp " + p1?.javaClass?.name + p2)
}
override fun onAuthenticationResponse(p0: SAPeerAgent?, p1: SAAuthenticationToken?, p2: Int) {
super.onAuthenticationResponse(p0, p1, p2)
App.d(TAG, "Auth " + p1.toString())
}
override protected fun onServiceConnectionRequested(agent: SAPeerAgent) {
}
}
override fun onFindPeerAgentsResponse(peerAgents: Array<SAPeerAgent>?, result: Int) {
}
override fun onError(peerAgent: SAPeerAgent?, errorMessage: String?, errorCode: Int) {
super.onError(peerAgent, errorMessage, errorCode)
}
override fun onPeerAgentsUpdated(peerAgents: Array<SAPeerAgent>?, result: Int) {
}
}
As you see, MessageJobs requires MessageSocket class as well that you would need to implement and that processes all messages coming from your device.
Bottom line, it's not that simple and it requires some digging to internals and coding, but it works, and most importantly - it doesn't crash.
Your app will crash if you call Context.startForegroundService(...) and then call Context.stopService(...) before Service.startForeground(...) is called.
I have a clear repro here ForegroundServiceAPI26
I have opened a bug on this at : Google issue tracker
Several bugs on this have been opened and closed Won't Fix.
Hopefully mine with clear repro steps will make the cut.
Information provided by google team
Google issue tracker Comment 36
This is not a framework bug; it's intentional. If the app starts a service instance with startForegroundService(), it must transition that service instance to the foreground state and show the notification. If the service instance is stopped before startForeground() is called on it, that promise is unfulfilled: this is a bug in the app.
Re #31, publishing a Service that other apps can start directly is fundamentally unsafe. You can mitigate that a bit by treating all start actions of that service as requiring startForeground(), though obviously that may not be what you had in mind.
Google issue tracker Comment 56
There are a couple of different scenarios that lead to the same outcome here.
The outright semantic issue, that it's simply an error to kick something off with startForegroundService() but neglect to actually transition it to foreground via startForeground(), is just that: a semantic issue. That's treated as an app bug, intentionally. Stopping the service before transitioning it to foreground is an app error. That was the crux of the OP, and is why this issue has been marked "working as intended."
However, there are also questions about spurious detection of this problem. That's is being treated as a genuine problem, though it's being tracked separately from this particular bug tracker issue. We aren't deaf to the complaint.
Since everybody visiting here is suffering the same thing, I want to share my solution that nobody else has tried before (in this question anyways). I can assure you that it is working, even on a stopped breakpoint which confirms this method.
The issue is to call Service.startForeground(id, notification) from the service itself, right? Android Framework unfortunately does not guarantee to call Service.startForeground(id, notification) within Service.onCreate() in 5 seconds but throws the exception anyway, so I've come up with this way.
Bind the service to a context with a binder from the service before calling Context.startForegroundService()
If the bind is successful, call Context.startForegroundService() from the service connection and immediately call Service.startForeground() inside the service connection.
IMPORTANT NOTE: Call the Context.bindService() method inside a try-catch because in some occasions the call can throw an exception, in which case you need to rely on calling Context.startForegroundService() directly and hope it will not fail. An example can be a broadcast receiver context, however getting application context does not throw an exception in that case, but using the context directly does.
This even works when I'm waiting on a breakpoint after binding the service and before triggering the "startForeground" call. Waiting between 3-4 seconds do not trigger the exception while after 5 seconds it throws the exception. (If the device cannot execute two lines of code in 5 seconds, then it's time to throw that in the trash.)
So, start with creating a service connection.
// Create the service connection.
ServiceConnection connection = new ServiceConnection()
{
#Override
public void onServiceConnected(ComponentName name, IBinder service)
{
// The binder of the service that returns the instance that is created.
MyService.LocalBinder binder = (MyService.LocalBinder) service;
// The getter method to acquire the service.
MyService myService = binder.getService();
// getServiceIntent(context) returns the relative service intent
context.startForegroundService(getServiceIntent(context));
// This is the key: Without waiting Android Framework to call this method
// inside Service.onCreate(), immediately call here to post the notification.
myService.startForeground(myNotificationId, MyService.getNotification());
// Release the connection to prevent leaks.
context.unbindService(this);
}
#Override
public void onBindingDied(ComponentName name)
{
Log.w(TAG, "Binding has dead.");
}
#Override
public void onNullBinding(ComponentName name)
{
Log.w(TAG, "Bind was null.");
}
#Override
public void onServiceDisconnected(ComponentName name)
{
Log.w(TAG, "Service is disconnected..");
}
};
Inside your service, create a binder that returns the instance of your service.
public class MyService extends Service
{
public class LocalBinder extends Binder
{
public MyService getService()
{
return MyService.this;
}
}
// Create the instance on the service.
private final LocalBinder binder = new LocalBinder();
// Return this instance from onBind method.
// You may also return new LocalBinder() which is
// basically the same thing.
#Nullable
#Override
public IBinder onBind(Intent intent)
{
return binder;
}
}
Then, try to bind the service from that context. If it succeeds, it will call ServiceConnection.onServiceConnected() method from the service connection that you're using. Then, handle the logic in the code that's shown above. An example code would look like this:
// Try to bind the service
try
{
context.bindService(getServiceIntent(context), connection,
Context.BIND_AUTO_CREATE);
}
catch (RuntimeException ignored)
{
// This is probably a broadcast receiver context even though we are calling getApplicationContext().
// Just call startForegroundService instead since we cannot bind a service to a
// broadcast receiver context. The service also have to call startForeground in
// this case.
context.startForegroundService(getServiceIntent(context));
}
It seems to be working on the applications that I develop, so it should work when you try as well.
Now in Android O you can set the background limitation as below
The service which is calling a service class
Intent serviceIntent = new Intent(SettingActivity.this,DetectedService.class);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
SettingActivity.this.startForegroundService(serviceIntent);
} else {
startService(serviceIntent);
}
and the service class should be like
public class DetectedService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public void onCreate() {
super.onCreate();
int NOTIFICATION_ID = (int) (System.currentTimeMillis()%10000);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForeground(NOTIFICATION_ID, new Notification.Builder(this).build());
}
// Do whatever you want to do here
}
}
I have a widget which does relatively frequent updates when the device is awake and I was seeing thousands of crashes in just a few days.
The issue trigger
I even noticed the issue even on my Pixel 3 XL when I wouldn't have thought the device to have much load at all. And any and all code paths were covered with startForeground(). But then I realized that in many cases my service gets the job done really quickly. I believe the trigger for my app was that the service was finishing before the system actually got around to showing a notification.
The workaround/solution
I was able to get rid of all crashes. What I did was to remove the call to stopSelf(). (I was thinking about delaying the stop until I was pretty sure the notification was shown, but I don't want the user to see the notification if it isn't necessary.) When the service has been idle for a minute or the system destroys it normally without throwing any exceptions.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
stopForeground(true);
} else {
stopSelf();
}
I have a work around for this problem. I have verified this fix in my own app(300K+ DAU), which can reduce at least 95% of this kind of crash, but still cannot 100% avoid this problem.
This problem happens even when you ensure to call startForeground() just after service started as Google documented. It may be because the service creation and initialization process already cost more than 5 seconds in many scenarios, then no matter when and where you call startForeground() method, this crash is unavoidable.
My solution is to ensure that startForeground() will be executed within 5 seconds after startForegroundService() method, no matter how long your service need to be created and initialized. Here is the detailed solution.
Do not use startForegroundService at the first place, use bindService() with auto_create flag. It will wait for the service initialization. Here is the code, my sample service is MusicService:
final Context applicationContext = context.getApplicationContext();
Intent intent = new Intent(context, MusicService.class);
applicationContext.bindService(intent, new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder binder) {
if (binder instanceof MusicBinder) {
MusicBinder musicBinder = (MusicBinder) binder;
MusicService service = musicBinder.getService();
if (service != null) {
// start a command such as music play or pause.
service.startCommand(command);
// force the service to run in foreground here.
// the service is already initialized when bind and auto_create.
service.forceForeground();
}
}
applicationContext.unbindService(this);
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
}, Context.BIND_AUTO_CREATE);
Then here is MusicBinder implementation:
/**
* Use weak reference to avoid binder service leak.
*/
public class MusicBinder extends Binder {
private WeakReference<MusicService> weakService;
/**
* Inject service instance to weak reference.
*/
public void onBind(MusicService service) {
this.weakService = new WeakReference<>(service);
}
public MusicService getService() {
return weakService == null ? null : weakService.get();
}
}
The most important part, MusicService implementation, forceForeground() method will ensure that startForeground() method is called just after service start:
public class MusicService extends MediaBrowserServiceCompat {
...
private final MusicBinder musicBind = new MusicBinder();
...
#Override
public IBinder onBind(Intent intent) {
musicBind.onBind(this);
return musicBind;
}
...
public void forceForeground() {
// API lower than 26 do not need this work around.
if (Build.VERSION.SDK_INT >= 26) {
Notification notification = mNotificationHandler.createNotification(this);
// call startForeground just after service start.
startForeground(Constants.NOTIFICATION_ID, notification);
}
}
}
If you want to run the step 1 code snippet in a pending intent, such as if you want to start a foreground service in a widget (a click on widget button) without opening your app, you can wrap the code snippet in a broadcast receiver, and fire a broadcast event instead of start service command.
That is all. Hope it helps. Good luck.
Just a heads up as I wasted way too many hours on this. I kept getting this exception even though I was calling startForeground(..) as the first thing in onCreate(..).
In the end I found that the problem was caused by using NOTIFICATION_ID = 0. Using any other value seems to fix this.
You have to add a permission as bellow for android 9 device when use target sdk 28 or later or the exception will always happen:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
This error also occurs on Android 8+ when Service.startForeground(int id, Notification notification) is called while id is set to 0.
id int: The identifier for this notification as per NotificationManager.notify(int, Notification); must not be 0.
So many answer but none worked in my case.
I have started service like this.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent);
} else {
startService(intent);
}
And in my service in onStartCommand
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
.setContentTitle(getString(R.string.app_name))
.setContentText("SmartTracker Running")
.setAutoCancel(true);
Notification notification = builder.build();
startForeground(NOTIFICATION_ID, notification);
} else {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setContentTitle(getString(R.string.app_name))
.setContentText("SmartTracker is Running...")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true);
Notification notification = builder.build();
startForeground(NOTIFICATION_ID, notification);
}
And don't forgot to set NOTIFICATION_ID non zero
private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
private static final int NOTIFICATION_ID = 555;
SO everything was perfect but still crashing on 8.1 so cause was as below.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
stopForeground(true);
} else {
stopForeground(true);
}
I have called stop foreground with remove notificaton but once notification removed service become background and background service can not run in android O from background. started after push received.
So magical word is
stopSelf();
So far so any reason your service is crashing follow all above steps and enjoy.
Please don't call any StartForgroundServices inside onCreate() method, you have to call StartForground services in onStartCommand() after make the worker thread otherwise you will get ANR always , so please don't write complex login in main thread of onStartCommand();
public class Services extends Service {
private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
.setContentTitle(getString(R.string.app_name))
.setContentText("SmartTracker Running")
.setAutoCancel(true);
Notification notification = builder.build();
startForeground(1, notification);
Log.e("home_button","home button");
} else {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setContentTitle(getString(R.string.app_name))
.setContentText("SmartTracker is Running...")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setAutoCancel(true);
Notification notification = builder.build();
startForeground(1, notification);
Log.e("home_button_value","home_button_value");
}
return super.onStartCommand(intent, flags, startId);
}
}
EDIT: Caution! startForeground function can't take 0 as first argument, it will raise an exception! this example contains wrong function call, change 0 to your own const which couldnt be 0 or be greater than Max(Int32)
I've been researching this issue and this is what I've discovered so far. This crash could happen if we have code similar to this:
MyForegroundService.java
public class MyForegroundService extends Service {
#Override
public void onCreate() {
super.onCreate();
startForeground(...);
}
}
MainActivity.java
Intent serviceIntent = new Intent(this, MyForegroundService.class);
startForegroundService(serviceIntent);
...
stopService(serviceIntent);
The exception is thrown in the following block of the code:
ActiveServices.java
private final void bringDownServiceLocked(ServiceRecord r) {
...
if (r.fgRequired) {
Slog.w(TAG_SERVICE, "Bringing down service while still waiting for start foreground: "
+ r);
r.fgRequired = false;
r.fgWaiting = false;
mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
mAm.mHandler.removeMessages(
ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
if (r.app != null) {
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);
msg.obj = r.app;
msg.getData().putCharSequence(
ActivityManagerService.SERVICE_RECORD_KEY, r.toString());
mAm.mHandler.sendMessage(msg);
}
}
...
}
This method is executed before onCreate() of MyForegroundService because Android schedules the creation of the service on the main thread handler but bringDownServiceLocked is called on a BinderThread, wich is a race condition. It means that MyForegroundService didn't have a chance to call startForeground which will cause the crash.
To fix this we have to make sure that bringDownServiceLocked is not called before onCreate() of MyForegroundService.
public class MyForegroundService extends Service {
private static final String ACTION_STOP = "com.example.MyForegroundService.ACTION_STOP";
private final BroadcastReceiver stopReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
context.removeStickyBroadcast(intent);
stopForeground(true);
stopSelf();
}
};
#Override
public void onCreate() {
super.onCreate();
startForeground(...);
registerReceiver(
stopReceiver, new IntentFilter(ACTION_STOP));
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(stopReceiver);
}
public static void stop(Context context) {
context.sendStickyBroadcast(new Intent(ACTION_STOP));
}
}
By using sticky broadcasts we make sure that the broadcast doesn't get lost and stopReceiver receives the stop intent as soon as it has been registered in onCreate() of MyForegroundService. By this time we have already called startForeground(...). We also have to remove that sticky broadcast to prevent stopReceiver being notified next time.
Please note that the method sendStickyBroadcast is deprecated and I use it only as a temporary workaround to fix this issue.
Around 10 users is getting this error in crashlytics for our application.
As Kimi Chiu replied- The main cause of this problem is the service was stopped before it was promoted to the foreground. But the assertion didn't stop after the service get destroyed. You can try to reproduce this by adding StopService after calling startForegroundService-Kimi Chiu
So I tested this and was able to reproduce.
One solution I applied is, I am letting the service to stay for at least 5 seconds so that the service will promote to the foreground. And now I cannot reproduce the issue while testing.
private fun stopService() {
lifecycleScope.launch {
delay(5000L)
try {
stopForeground(true)
isForeGroundService = false
stopSelf()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
Lets see if the issue is reproduce in our next build.
Update :)-> This time there was no issue related to Context.startForegroundService() did not then call Service.startForeground()
Before/After comparission->
Before->
After->
From Google's docs on Android 12 behavior changes:
To provide a streamlined experience for short-running foreground services on Android 12, the system can delay the display of foreground service notifications by 10 seconds for certain foreground services. This change gives short-lived tasks a chance to complete before their notifications appear.
Solution: Call startForeground() in onCreate() for the Service which you use Context.startForegroundService()
https://developer.android.com/reference/android/content/Context.html#startForegroundService(android.content.Intent)
Similar to startService(Intent), but with an implicit promise that the
Service will call startForeground(int, android.app.Notification) once
it begins running. The service is given an amount of time comparable
to the ANR interval to do this, otherwise the system will
automatically stop the service and declare the app ANR.
Unlike the ordinary startService(Intent), this method can be used at
any time, regardless of whether the app hosting the service is in a
foreground state.
make sure you call the Service.startForeground(int, android.app.Notification) on the onCreate() so you ensure it will be called..if you have any condition that may prevent you from doing that, then you'd better off using the normal Context.startService(Intent) and call the Service.startForeground(int, android.app.Notification) yourself.
It seems that the Context.startForegroundService() adds a watchdog to make sure you called the Service.startForeground(int, android.app.Notification) before it was destroyed...
I am facing same issue and after spending time found a solutons you can try below code. If your using Service then put this code in onCreate else your using Intent Service then put this code in onHandleIntent.
if (Build.VERSION.SDK_INT >= 26) {
String CHANNEL_ID = "my_app";
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
"MyApp", NotificationManager.IMPORTANCE_DEFAULT);
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("")
.setContentText("").build();
startForeground(1, notification);
}
Problem With Android O API 26
If you stop the service right away (so your service does not actually really runs (wording / comprehension) and you are way under the ANR interval, you still need to call startForeground before stopSelf
https://plus.google.com/116630648530850689477/posts/L2rn4T6SAJ5
Tried this Approach But it Still creates an error:-
if (Util.SDK_INT > 26) {
mContext.startForegroundService(playIntent);
} else {
mContext.startService(playIntent);
}
I Am Using this until the Error is Resolved
mContext.startService(playIntent);
Even after calling the startForeground in Service, It crashes on some devices if we call stopService just before onCreate is called.
So, I fixed this issue by Starting the service with an additional flag:
Intent intent = new Intent(context, YourService.class);
intent.putExtra("request_stop", true);
context.startService(intent);
and added a check in onStartCommand to see if it was started to stop:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//call startForeground first
if (intent != null) {
boolean stopService = intent.getBooleanExtra("request_stop", false);
if (stopService) {
stopSelf();
}
}
//Continue with the background task
return START_STICKY;
}
P.S. If the service were not running, it would start the service first, which is an overhead.
Updating Data in onStartCommand(...)
onBind(...)
onBind(...) is a better lifecycle event to initiate startForeground vs. onCreate(...) because onBind(...) passes in an Intent which may contain important data in the Bundle needed to initialize the Service. However, it is not necessary as onStartCommand(...) is called when the Service is created for the first time or called subsequent times after.
onStartCommand(...)
startForeground in onStartCommand(...) is important in order to update the Service once it has already been created.
When ContextCompat.startForegroundService(...) is called after a Service has been created onBind(...) and onCreate(...) are not called. Therefore, updated data can be passed into onStartCommand(...) via the Intent Bundle to update data in the Service.
Sample
I'm using this pattern to implement the PlayerNotificationManager in the Coinverse cryptocurrency news app.
Activity / Fragment.kt
context?.bindService(
Intent(context, AudioService::class.java),
serviceConnection, Context.BIND_AUTO_CREATE)
ContextCompat.startForegroundService(
context!!,
Intent(context, AudioService::class.java).apply {
action = CONTENT_SELECTED_ACTION
putExtra(CONTENT_SELECTED_KEY, contentToPlay.content.apply {
audioUrl = uri.toString()
})
})
AudioService.kt
private var uri: Uri = Uri.parse("")
override fun onBind(intent: Intent?) =
AudioServiceBinder().apply {
player = ExoPlayerFactory.newSimpleInstance(
applicationContext,
AudioOnlyRenderersFactory(applicationContext),
DefaultTrackSelector())
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
intent?.let {
when (intent.action) {
CONTENT_SELECTED_ACTION -> it.getParcelableExtra<Content>(CONTENT_SELECTED_KEY).also { content ->
val intentUri = Uri.parse(content.audioUrl)
// Checks whether to update Uri passed in Intent Bundle.
if (!intentUri.equals(uri)) {
uri = intentUri
player?.prepare(ProgressiveMediaSource.Factory(
DefaultDataSourceFactory(
this,
Util.getUserAgent(this, getString(app_name))))
.createMediaSource(uri))
player?.playWhenReady = true
// Calling 'startForeground' in 'buildNotification(...)'.
buildNotification(intent.getParcelableExtra(CONTENT_SELECTED_KEY))
}
}
}
}
return super.onStartCommand(intent, flags, startId)
}
// Calling 'startForeground' in 'onNotificationStarted(...)'.
private fun buildNotification(content: Content): Unit? {
playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
this,
content.title,
app_name,
if (!content.audioUrl.isNullOrEmpty()) 1 else -1,
object : PlayerNotificationManager.MediaDescriptionAdapter {
override fun createCurrentContentIntent(player: Player?) = ...
override fun getCurrentContentText(player: Player?) = ...
override fun getCurrentContentTitle(player: Player?) = ...
override fun getCurrentLargeIcon(player: Player?,
callback: PlayerNotificationManager.BitmapCallback?) = ...
},
object : PlayerNotificationManager.NotificationListener {
override fun onNotificationStarted(notificationId: Int, notification: Notification) {
startForeground(notificationId, notification)
}
override fun onNotificationCancelled(notificationId: Int) {
stopForeground(true)
stopSelf()
}
})
return playerNotificationManager.setPlayer(player)
}
Ok, something I noticed on this that might help a few others too. This is strictly from testing to see if I could figure out how to fix the occurrences I am seeing. For simplicity sake, let's say I have a method that calls this from the presenter.
context.startForegroundService(new Intent(context, TaskQueueExecutorService.class));
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
This will crash with the same error. The Service will NOT start until the method is complete, therefore no onCreate() in the service.
So even if you update the UI off the main thread, IF you have anything that might hold up that method after it, it won't start on time and give you the dreaded Foreground Error. In my case we were loading some things onto a queue and each called startForegroundService, but some logic was involved with each in the background. So if the logic took too long to finish that method since they were called back to back, crash time. The old startService just ignored it and went on it's way and since we called it each time, the next round would finish up.
This left me wondering, if I called the service from a thread in the background, could it not be fully bound on the start and run immediately, so I started experimenting. Even though this does NOT start it immediately, it does not crash.
new Handler(Looper.getMainLooper()).post(new Runnable() {
public void run() {
context.startForegroundService(new Intent(context,
TaskQueueExecutorService.class));
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
I will not pretend to know why it does not crash although I suspect this forces it to wait until the main thread can handle it in a timely fashion. I know it's not ideal to tie it to the main thread, but since my usage calls it in the background, I'm not real concerned if it waits until it can complete rather than crash.
I am adding some code in #humazed answer. So there in no initial notification. It might be a workaround but it works for me.
#Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= 26) {
String CHANNEL_ID = "my_channel_01";
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT);
((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("")
.setContentText("")
.setColor(ContextCompat.getColor(this, R.color.transparentColor))
.setSmallIcon(ContextCompat.getColor(this, R.color.transparentColor)).build();
startForeground(1, notification);
}
}
I am adding transparentColor in small icon and color on notification.
It will work.
One issue might be Service class is not enabled in AndroidManifest file.
Please check it as well.
<service
android:name=".AudioRecorderService"
android:enabled="true"
android:exported="false"
android:foregroundServiceType="microphone" />
I had an issue in Pixel 3, Android 11 that when my service was running very short, then the foreground notification was not dismissed.
Adding 100ms delay before stopForeground() stopSelf() seems to help.
People write here that stopForeground() should be called before stopSelf(). I cannot confirm, but I guess it doesn't bother to do that.
public class AService extends Service {
#Override
public void onCreate() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(
getForegroundNotificationId(),
channelManager.buildBackgroundInfoNotification(getNotificationTitle(), getNotificationText()),
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC);
} else {
startForeground(getForegroundNotificationId(),
channelManager.buildBackgroundInfoNotification(getNotificationTitle(), getNotificationText())
);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground();
if (hasQueueMoreItems()) {
startWorkerThreads();
} else {
stopForeground(true);
stopSelf();
}
return START_STICKY;
}
private class WorkerRunnable implements Runnable {
#Override
public void run() {
while (getItem() != null && !isLoopInterrupted) {
doSomething(getItem())
}
waitALittle();
stopForeground(true);
stopSelf();
}
private void waitALittle() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
I just check the PendingIntent null or nor not before calling the
context.startForegroundService(service_intent) function.
this works for me
PendingIntent pendingIntent=PendingIntent.getBroadcast(context,0,intent,PendingIntent.FLAG_NO_CREATE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && pendingIntent==null){
context.startForegroundService(service_intent);
}
else
{
context.startService(service_intent);
}
}
just call startForeground method immediately after Service or IntentService is Created. like this:
import android.app.Notification;
public class AuthenticationService extends Service {
#Override
public void onCreate() {
super.onCreate();
startForeground(1,new Notification());
}
}
I have fixed the problem with starting the service with startService(intent) instead of Context.startForeground() and calling startForegound() immediately after super.OnCreate(). Additionally, if you starting service on boot, you can start Activity that starts service on the boot broadcast. Although it is not a permanent solution, it works.
I just sharing my review about this. I am not surely(100% telling) that above code is not working for me and other guys also but some times I got this issue. Suppose I run the app 10 time then might be got this issue 2 to 3 three time.
I have tried above all the answers but still not solve the issue. I have implemented above all the codes and tested in different api levels (API level 26, 28, 29) and difference mobile (Samsung, Xiaomi, MIUI, Vivo, Moto, One Plus, Huawei, etc ) and getting same below issue.
Context.startForegroundService() did not then call Service.startForeground();
I have read service on google developer web site, some other blog and some stack overflow question and got the idea that this issue will happen when we call startForgroundSerivce() method but at that time service was not started.
In my case I have stop the service and after immediately start service. Below is the hint.
....//some other code
...// API level and other device auto star service condition is already set
stopService();
startService();
.....//some other code
In this case service is not started due to processing speed and low memory in RAM but startForegroundService() method is called and fire the exception.
Work for me:
new Handler().postDelayed(()->ContextCompat.startForegroundService(activity, new Intent(activity, ChatService.class)), 500);
I have change code and set 500 milliseconds delay to call startService() method and issue is solved. This is not perfect solution because this way app's performance goes downgrade.
Note:
This is only for Foreground and Background service only. Don't tested when using Bind service.
I am sharing this because only this is the way I have solved this issue.

Categories

Resources