Android O - 旧的启动前台服务仍在运行?

时间:2017-04-06 09:46:47

标签: android location android-8.0-oreo

因此,对于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

startForeground方法似乎没有问题,无论是针对API级别25定位和编译还是针对O进行编译和编译(如此处所指示的那样:https://developer.android.com/preview/migration.html#uya

所以,重现:

  1. 按照上一个链接
  2. 中的说明配置应用程序gradle
  3. 打开应用
  4. 请求位置更新
  5. 关闭应用程序(通过后退按钮或主页按钮)
  6. 服务正在前台运行(通知阴影中的图标显示)。即使在运行O的设备上,位置更新也会按预期(每10秒)完成。我在这里缺少什么?

8 个答案:

答案 0 :(得分:40)

这对我有用。

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

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

希望它有效!

答案 1 :(得分:5)

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

答案 2 :(得分:5)

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

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

服务启动后,使用与Android docs say类似的代码创建通知渠道,然后创建构建并使用它:

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

答案 3 :(得分:3)

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

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

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

答案 4 :(得分:3)

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

来自提交消息:

  

而不是要求提供先验通知   启动服务直接进入前台状态,我们采用两阶段   即使从a开始进行持续服务工作的复合操作   后台执行状态。 Context#startForegroundService()不是   受背景限制,并要求   service正式通过startForeground()进入前台状态   5秒。如果服务不这样做,它将被操作系统停止并且   应用程序被归咎于服务ANR。

答案 5 :(得分: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())

答案 6 :(得分: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);    

答案 7 :(得分: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)
}

}