Android O – 旧的启动前台服务仍然有效吗?

因此,对于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进行编译(如下所示: https : //developer.android.com/preview/migration.html#uya ),startForeground方法似乎都没有问题。

所以,重现:

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

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

  • 基于位置的增强现实Android SDK
  • ACCESS_FINE_LOCATION AndroidManifest权限未被授予
  • 保存在服务器上的位置的接近警报
  • 找不到com.google.android.gms.location.LocationClient类(android)
  • 如何在Android中通过纬度和经度获取城市名称?
  • 在Android模拟器中激活网络位置提供程序?
  • 无论你在哪里捏缩放android,都要保持地图居中
  • 令人难以置信的GPS点数不准确。 是什么原因?
  • 这对我有用。

    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()的 Service类中执行如下操作
     @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中,您才需要提供渠道ID,这是Android Oreo中的新function。

    希望它有效!

    通常,您使用startService从广播接收器启动服务。 他们说,调用startService是不可能的(或可靠的),因为现在有背景限制,所以你需要调用startServiceInForeground 。 然而,从文档来看,它发生的时间并不是很清楚,因为应用程序在收到广播意图时被列入白名单,因此不清楚startService何时抛出IllegalStateException

    当应用程序位于前台时,启动前台服务的传统方式仍然有效但是针对API级别26 / Android O的应用程序启动前台服务的推荐方法是使用新引入的NotificationManager#startServiceInForeground方法来创建前台服务首先。

    由于Android O的后台执行限制,如果应用程序处于后台模式,在后台启动服务然后将其提升到前台的旧方法将无效。

    此处记录了迁移过程和步骤。 https://developer.android.com/preview/features/background.html#migration

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

     ContextCompat.startForegroundService(context, serviceClass); 

    服务启动后,使用类似于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()); 

    同样@Kislingk在评论中提到NotificationManager.startServiceInForeground已被删除。 它被提交08992ac标记为已弃用。

    从提交消息:

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

    如果某些需要使用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())