Context.startForegroundService did not then call Service.startForeground - android

It's mine BroadcastReciever class. The class working on Boot phone status.
Code ;
public class BroadCastRecieverBoot extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent ıntent) {
if(Intent.ACTION_BOOT_COMPLETED.equals(ıntent.getAction()))
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(new Intent(context, MyService.class));
context.startForegroundService(new Intent(context, GPSTracker.class));
} else {
context.startService(new Intent(context, MyService.class));
context.startService(new Intent(context, GPSTracker.class));
}
}
}
}
I get This Error ;
android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1792)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6523)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:857)
It doesn't work on Android Oreo now. I don't know what is the mistake of that.

According to official document of Android 8.0 Background Execution Limits
Android 8.0 introduces the new method
startForegroundService() to start a new service in the foreground.
After the system has created the service, the app has five seconds to
call the service's startForeground() method to show the new service's
user-visible notification. If the app does not call startForeground()
within the time limit, the system stops the service and declares the
app to be ANR.
So, make sure you have started ongoing notification by calling startForeground (int id, Notification notification) in the onCreate() method of your service.
Note: Apps targeting API Build.VERSION_CODES.P or later must request the permission Manifest.permission.FOREGROUND_SERVICE in order to use this API.

Related

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

I need your help to resolve this problem :
Context.startForegroundService() did not then call Service.startForeground()
I'm using a BroadcastReceiver to start the notification service :
#Override
public void onReceive(Context context, Intent intent) {
WakeLock.acquire(context);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ContextCompat.startForegroundService(context, new Intent(context, StartNotificationService.class).putExtras(intent));
} else {
context.startService(new Intent(context, StartNotificationService.class).putExtras(intent));
}
}
Have you any idea please ? How can I resolve this issue ?
Thanks you
Your Service must call startForeground() when it is started, otherwise the system will not allow it to run.

How to stop a foreground service before calling startForeground() method?

I am starting a foreground service using the following code
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
This starts a foreground service. In Android Oreo and above, we must call startForeground() method within 5 seconds. Now in my service, we do some processing and if certain conditions are not met, then I don't call startForeground() method. Instead, I try to kill the service.
if (someConditionMet) {
startForeground(notificationId, notification)
} else {
stopSelf()
}
Now if someConditionMet variable is false, then my app crashes with the following crash log.
2019-04-10 16:57:25.695 19785-19785/com.awesomedroidapps.foregroundactivity E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.awesomedroidapps.foregroundactivity, PID: 19785
android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{e78d43f u0 com.awesomedroidapps.foregroundactivity/.ForegroundServiceTest}
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1835)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6863)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
I don't understand that when I am stopping the service then why is it necessary to call startForeground() method? Is there any way we can stop the foreground service without calling startForeground() method?
You can't. Period.
You can't stop a foreground service before the startForeground has been called and successfully executed. It is mandatory to finish this call before any action in the foreground service is taken.
No matter what combinations of stopSelf() and stopForeground() you use, it would not do what you desire to do.
Here is a really nice article about the same:
Foreground Service Pitfalls
If you want to stop service without creating actual notification you can just invoke startForeground with stub notification and cancel in right after stopSelf is invoked.
if (conditionMet)
startForeground(notificationId, notification)
} else {
startForeground(STUB_NOTIFICATION_ID, createStubNotification())
stopSelf()
cancelStubNotification()
}
private fun createStubNotification(): Notification {
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
notificationManager.createNotificationChannel(
NotificationChannel(
STUB_NOTIFICATION_CHANNEL_ID,
STUB_NOTIFICATION_CHANNEL_NAME,
IMPORTANCE_LOW
).apply { setShowBadge(false) }
)
}
return with(NotificationCompat.Builder(this, STUB_NOTIFICATION_CHANNEL_ID)) {
setBadgeIconType(NotificationCompat.BADGE_ICON_NONE)
setOngoing(true)
setSmallIcon(R.drawable.ic_notification)
priority = NotificationCompat.PRIORITY_LOW
setShowWhen(false)
setSilent(true)
build()
}.also { notificationManager.notify(STUB_NOTIFICATION_ID, it) }
}
private fun cancelStubNotification() =
(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager)
.cancel(STUB_NOTIFICATION_ID)
Try this: Use a static variable and track the state of your service i.e running or not running, then check the state before calling stopself(). When the service is started, update the boolean flag to true and when it stops, update the boolean flag to false.

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.

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

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