Android O - Old start foreground service still working?

孤街醉人 提交于 2019-12-17 04:28:39

问题


So, with Android O, you need to have your service running as a foreground service if you want to receive more than just a few location updates per hour.

I noticed that the old method for starting a foreground service does seem to work on O. i.e.

startForeground(NOTIFICATION_ID, getNotification());

According to the behaviour changes guide here: https://developer.android.com/preview/behavior-changes.html

The NotificationManager.startServiceInForeground() method starts a foreground service. The old way to start a foreground service no longer works.

Though the new method only works when targeting O, it seems that the old method still seems to work on an O device whether targeting O or not.

Edit Including example:

The Google sample project LocationUpdatesForegroundService actually has a working example where you can see the issue first hand. https://github.com/googlesamples/android-play-location/tree/master/LocationUpdatesForegroundService

The startForeground method seems to work without issue whether targeting and compiling against API level 25 OR targeting and compiling against O (as directed to here: https://developer.android.com/preview/migration.html#uya)

So, to reproduce:

  1. Configure the app gradle as mentioned in the previous link
  2. Open the app
  3. Request location updates
  4. Close app (either via back button or home button)

Service is running in foreground (shown by icon in notification shade). Location updates are coming through as expected (every 10 seconds) even on a device running O. What I am missing here?


回答1:


This worked for me.

  1. In Activity class, start service using startForegroundService() instead of startService()
    Intent myService = new Intent(this, MyService.class);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        startForegroundService(myService);
    } else {
        startService(myService);
    }
  1. Now in Service class in onStartCommand() do as following
@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(text)
                .setAutoCancel(true);

        Notification notification = builder.build();
        startForeground(1, notification);

    } else {

        NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                .setContentTitle(getString(R.string.app_name))
                .setContentText(text)
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .setAutoCancel(true);

        Notification notification = builder.build();

        startForeground(1, notification);
    }
    return START_NOT_STICKY;
}

Note: Using Notification.Builder instead of NotificationCompat.Builder made it work. Only in Notification.Builder you will need to provide Channel ID which is new feature in Android Oreo.

Hope it works!




回答2:


Usually you start your service from a broadcast receiver using startService. They are saying that it's no more possible (or reliable) to call startService because there are now background limitations, so you need to call startServiceInForeground instead. However from docs it's not really clear when it happens because the app is whitelisted when it receives a broadcast intent, so it's not clear exactly when startService throws IllegalStateException.




回答3:


In the Activity (or any context that starts the foreground service), call this:

Intent intent = new Intent(this, MyService.class)
ContextCompat.startForegroundService(context, intent);

When the service has started, create a notification channel using similar code to what Android docs say, and then create a build and use it:

final Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID).setSmallIcon(...)//
            .setPriority(...).setCategory(...).setContentTitle(...).setContentText(...).setTicker(...);
// and maybe other preparations to the notification...
startForeground(notificationId, builder.build());



回答4:


The legacy way of starting a foreground service still works when the app is in the foreground but the recommended way to start a foreground service for Apps targeting API level 26/Android O is to use the newly introduced NotificationManager#startServiceInForeground method to create a foreground service in the first place.

The old way of starting the service in the background and then promoting it to the foreground will not work if the app is in background mode, because of the background execution limitations of Android O.

Migration process and steps are documented here. https://developer.android.com/preview/features/background.html#migration




回答5:


As also @Kislingk mentioned in a comment NotificationManager.startServiceInForeground was removed. It was marked as deprecated w/ the commit 08992ac.

From the commit message:

Rather than require an a-priori Notification be supplied in order to start a service directly into the foreground state, we adopt a two-stage compound operation for undertaking ongoing service work even from a background execution state. Context#startForegroundService() is not subject to background restrictions, with the requirement that the service formally enter the foreground state via startForeground() within 5 seconds. If the service does not do so, it is stopped by the OS and the app is blamed with a service ANR.




回答6:


I add sample if some need with backstack builder

    val notifyManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
    val playIntent    = Intent(this, this::class.java).setAction(PAUSE)
    val cancelIntent  = Intent(this, this::class.java).setAction(EXIT)

    val stop          = PendingIntent.getService(this, 1, playIntent, PendingIntent.FLAG_UPDATE_CURRENT)
    val exit          = PendingIntent.getService(this, 2, cancelIntent, PendingIntent.FLAG_UPDATE_CURRENT)

    val builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        notifyManager.createNotificationChannel(NotificationChannel(NOTIFICATION_ID_CHANNEL_ID, getString(R.string.app_name), NotificationManager.IMPORTANCE_HIGH))
        NotificationCompat.Builder(this, NOTIFICATION_ID_CHANNEL_ID)
    } else
        NotificationCompat.Builder(this)

    builder.apply {
        setContentTitle(station.name)
        setContentText(metaToText(meta) )
        setSmallIcon(R.drawable.ic_play_arrow_white_24px)
        setAutoCancel(false)
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) priority = Notification.PRIORITY_MAX
        addAction(R.drawable.ic_stop_white_24px, getString(R.string.player_notification_stop), stop)
        addAction(R.drawable.ic_close_white_24px, getString(R.string.player_notification_exit), exit)
    }

    val stackBuilder = TaskStackBuilder.create(this)
    stackBuilder.addParentStack(PlayerActivity::class.java)
    stackBuilder.addNextIntent(Intent(this, PlayerActivity::class.java))
    builder.setContentIntent(stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT))

    startForeground(NOTIFICATION_ID, builder.build())



回答7:


startForeground(1, notification); will work for on Android O but as per Android O requirement we have to show a persistent notification to the user. At the same time it might confuse the user in some cases (system notification about App running in background and impacting battery) and so user may uninstall the app. So best would be use newly introduce WorkManager class to schedule the task as foreground.

  1. Create your worker class (say MyWorker) by extending "Worker" class where you can perform the long running task. Override below method in this class:
    • doWork() [Required]
    • onStopped() [Optional]
    • onWorkFinished [Optional] etc.
  2. Create repeating/periodic [PeriodicWorkRequest] or non-repeating [OneTimeWorkRequest] work as per requirement.
  3. Get the instance of WorkManager and en queue the work.

Code snippet:

OneTimeWorkRequest work =
     new OneTimeWorkRequest.Builder(MyWorker.class)
 .build();
WorkManager.getInstance().enqueue(work);    


来源:https://stackoverflow.com/questions/43251528/android-o-old-start-foreground-service-still-working

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!