43

因此,对于 Android O,如果您希望每小时接收的不仅仅是几个位置更新,您需要让您的服务作为前台服务运行。

我注意到启动前台服务的旧方法似乎确实适用于 O。即

startForeground(NOTIFICATION_ID, getNotification());

根据此处的行为更改指南: https ://developer.android.com/preview/behavior-changes.html

NotificationManager.startServiceInForeground() 方法启动前台服务。启动前台服务的旧方法不再有效。

尽管新方法仅在针对 O 时有效,但似乎旧方法似乎仍然适用于 O 设备,无论是否针对 O。

编辑 包括示例:

Google 示例项目 LocationUpdatesForegroundService 实际上有一个工作示例,您可以在其中直接看到问题。 https://github.com/googlesamples/android-play-location/tree/master/LocationUpdatesForegroundService

无论是针对 API 级别 25 进行定位和编译,还是针对 O 进行定位和编译,startForeground 方法似乎都可以正常工作(如下所示:https ://developer.android.com/preview/migration.html#uya )

所以,要重现:

  1. 如上一个链接中所述配置应用程序gradle
  2. 打开应用程序
  3. 请求位置更新
  4. 关闭应用程序(通过后退按钮或主页按钮)

服务正在前台运行(由通知阴影中的图标显示)。即使在运行 O 的设备上,位置更新也会按预期进行(每 10 秒一次)。我在这里缺少什么?

4

8 回答 8

57

这对我有用。

  1. 在 Activity 类中,使用startForegroundService()而不是startService()启动服务
    Intent myService = new Intent(this, MyService.class);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        startForegroundService(myService);
    } else {
        startService(myService);
    }
  1. 现在在onStartCommand()的服务类中执行以下操作
@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;
}

注意:使用Notification.Builder而不是NotificationCompat.Builder使其工作。只有在 Notification.Builder 中,您才需要提供 Android Oreo 中的新功能 Channel ID。

希望它有效!

编辑:

如果您的目标 API 级别为 28 或更高,则需要 FOREGROUND_SERVICE 权限,否则您的应用将崩溃。

只需将其添加到 AndroidManifest.xml 文件即可。

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
于 2017-09-24T15:39:51.173 回答
13

在 Activity(或启动前台服务的任何上下文)中,调用它:

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

服务启动后,使用类似于Android 文档所说的代码创建通知通道,然后创建一个构建器并使用它:

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());
于 2018-04-07T20:54:42.430 回答
6

通常,您从广播接收器使用startService. 他们说startService因为现在有背景限制,所以不再可能(或可靠)打电话,所以你需要打电话startServiceInForeground。但是从文档中并不清楚它何时发生,因为应用程序在收到广播意图时被列入白名单,因此不清楚确切的时间startServicethrows IllegalStateException

于 2017-04-07T17:04:34.260 回答
3

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

于 2017-08-19T02:32:20.083 回答
3

正如评论中提到的@Kislingk 也NotificationManager.startServiceInForeground被删除。使用提交 08992ac将其标记为已弃用。

从提交消息:

我们不需要提供先验通知以将服务直接启动到前台状态,而是采用两阶段复合操作来进行正在进行的服务工作,即使是从后台执行状态。Context#startForegroundService()不受后台限制,要求服务在5秒内通过startForeground()正式进入前台状态。如果服务不这样做,它会被操作系统停止,并且应用程序会受到服务 ANR 的指责。

于 2018-01-16T08:58:07.717 回答
0

如果需要使用 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())
于 2017-12-22T19:30:07.633 回答
0

在 android O 中,android 有后台限制,所以我们必须管理或调用 startForegroundService(service) 方法而不是 startSetvice()

在清单中添加权限

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

// 我们像这样启动服务

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        var service = Intent(context, AnyService::class.java)
        context?.startForegroundService(service)
    } else {
        var service = Intent(context, AnyService::class.java)
        context?.startService(service)
    }

在 AnyService 类中

class AnyService : Service() {

override fun onBind(intent: Intent?): IBinder? {


}

override fun onCreate() {

    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
        startMyOwnForeground()
    else
        startForeground(1, Notification())

}


override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {


    return START_STICKY

}

override fun onDestroy() {
    super.onDestroy()
}


@RequiresApi(Build.VERSION_CODES.O)
private fun startMyOwnForeground() {
    val NOTIFICATION_CHANNEL_ID = "example.permanence"
    val channelName = "Background Service"
    val chan = NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE)
    chan.lightColor = Color.BLUE
    chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE

    val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    manager.createNotificationChannel(chan)

    val notificationBuilder = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
    val notification = notificationBuilder.setOngoing(true)
        .setContentTitle("App is running in background")
        .setPriority(NotificationManager.IMPORTANCE_MIN)
        .setCategory(Notification.CATEGORY_SERVICE)
        .build()
    startForeground(2, notification)
}

}

于 2020-01-22T09:53:19.867 回答
0

startForeground(1, 通知); 将适用于 Android O,但根据 Android O 要求,我们必须向用户显示持久通知。同时在某些情况下可能会混淆用户(关于应用程序在后台运行并影响电池的系统通知),因此用户可能会卸载应用程序。所以最好使用新引入的 WorkManager 类将任务安排为前台。

  1. 通过扩展“Worker”类来创建您的工作者类(比如 MyWorker),您可以在其中执行长时间运行的任务。在此类中覆盖以下方法:
    • doWork() [必填]
    • onStopped() [可选]
    • onWorkFinished [可选] 等。
  2. 根据要求创建重复/定期 [PeriodicWorkRequest] 或非重复 [OneTimeWorkRequest] 工作。
  3. 获取 WorkManager 的实例并将工作排队。

代码片段:

OneTimeWorkRequest work =
     new OneTimeWorkRequest.Builder(MyWorker.class)
 .build();
WorkManager.getInstance().enqueue(work);    
于 2018-09-29T13:48:56.217 回答