Context.startForegroundService() did not then call

2019-01-04 06:46发布

I am using Service Class on the Android O OS.

I plan to use the Service in the background.

The Android recommendation states that startService should use startForegroundService.

If you use startForegroundService, the Service throws a Context.startForegroundService() did not then call Service.startForeground() error.

What's wrong with this?

20条回答
Evening l夕情丶
2楼-- · 2019-01-04 07:23

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.

查看更多
Anthone
3楼-- · 2019-01-04 07:28

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.

  1. 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);
    
  2. 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();
         }
     }
    
  3. The most important part, MusicService implementation, forceForeground() method will ensure that startForeground() method is called just after startForegroundService():

    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) {
                Intent intent = new Intent(this, MusicService.class);
                // service has already been initialized.
                // startForeground method should be called within 5 seconds.
                ContextCompat.startForegroundService(this, intent);
                Notification notification = mNotificationHandler.createNotification(this);
                // call startForeground just after startForegroundService.
                startForeground(Constants.NOTIFICATION_ID, notification);
            }
        }
    }
    
  4. 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.

查看更多
做个烂人
4楼-- · 2019-01-04 07:28

Make sure that all code paths calls startForeground method , for example the code may generate an exception on the onCreate Method of your service that prevent the startForeground from being called

   @Override
    public void onCreate() {
try{
}
catch(Exception e)
{
}
finally{
    startForeground(1, notificationbuilder.build());

}
    }
查看更多
女痞
5楼-- · 2019-01-04 07:29

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...

查看更多
\"骚年 ilove
6楼-- · 2019-01-04 07:29

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();
}
查看更多
聊天终结者
7楼-- · 2019-01-04 07:31

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 actually started to stop:

@Override
public int onStartCommand(Intent intent,
    int flags, int startId) {

    //call startForeground first
    boolean stopService = false;
    if (intent != null) {
        stopService = intent.getBooleanExtra("request_stop", false);
    }
    if (stopService) {
        stopSelf();
        return START_STICKY;
    }
    //Continue with the background task
    return START_STICKY;
}

P.S. If the service was not actually running it will start the service first which is an overhead.

查看更多
登录 后发表回答