Android 安卓O-旧版启动前台服务仍在运行?

Android 安卓O-旧版启动前台服务仍在运行?,android,location,android-8.0-oreo,Android,Location,Android 8.0 Oreo,因此,使用安卓O,如果您希望每小时接收的位置更新不止几个,那么您需要将您的服务作为前台服务运行 我注意到,启动前台服务的旧方法似乎在O。 i、 e 根据此处的行为改变指南: 方法启动前台服务。启动前台服务的旧方法不再有效 尽管新方法仅在目标为O时有效,但无论是否针对O,旧方法似乎仍然适用于O设备 编辑 包括示例: Google示例项目LocationUpdatesForegroundsService实际上有一个工作示例,您可以直接看到这个问题。 无论是针对API级别25的目标和编译,还是针对

因此,使用安卓O,如果您希望每小时接收的位置更新不止几个,那么您需要将您的服务作为前台服务运行

我注意到,启动前台服务的旧方法似乎在O。 i、 e

根据此处的行为改变指南:

方法启动前台服务。启动前台服务的旧方法不再有效

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

编辑 包括示例:

Google示例项目LocationUpdatesForegroundsService实际上有一个工作示例,您可以直接看到这个问题。

无论是针对API级别25的目标和编译,还是针对O的目标和编译,startForeground方法似乎都可以正常工作(如下所述:)

因此,复制:

  • 如前一链接所述配置应用程序gradle
  • 打开应用程序
  • 请求位置更新
  • 关闭应用程序(通过后退按钮或主页按钮)

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

    通常您使用
    startService从广播接收器启动服务。他们说调用
    startService
    不再可能(或可靠),因为现在存在后台限制,所以您需要调用
    startServiceInForeground
    。然而,从文档中还不清楚什么时候会发生,因为当应用程序收到广播意图时会被列入白名单,因此,现在还不清楚什么时候启动前台服务会抛出非法状态异常。当应用程序位于前台时,启动前台服务的传统方法仍然有效,但建议针对API级别26/Android O的应用程序启动前台服务的方法是使用新引入的NotificationManager#首先启动ServiceInforeGround方法来创建前台服务

    由于Android O的后台执行限制,如果应用程序处于后台模式,那么在后台启动服务然后将其升级到前台的旧方法将不起作用

    这里记录了迁移过程和步骤

    这对我很有效

  • 在活动类中,使用startForegroundService()而不是startService()启动服务
  • 现在在onStartCommand()的服务类中执行以下操作
  • 注意:使用Notification.Builder而不是NotificationCompat.Builder使其工作正常。仅在Notification.Builder中,您需要提供频道ID,这是Android Oreo中的新功能

    希望它能起作用

    编辑: 如果您的目标API级别为28或更高,则需要前台服务权限,否则,您的应用程序将崩溃

    只需将其添加到AndroidManifest.xml文件中

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

    如果需要,我可以使用Backback 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())
    
    val notifyManager=getSystemService(通知服务)作为NotificationManager
    val playIntent=Intent(this,this::class.java).setAction(暂停)
    val cancelIntent=Intent(this,this::class.java).setAction(EXIT)
    val stop=pendingent.getService(this,1,playtintent,pendingent.FLAG_UPDATE_CURRENT)
    val exit=PendingEvent.getService(此,2,cancelIntent,PendingEvent.FLAG_更新_当前)
    val builder=if(Build.VERSION.SDK\u INT>=Build.VERSION\u code.O){
    notifyManager.createNotificationChannel(NotificationChannel(通知\u ID\u通道\u ID,getString(R.string.app\u名称),NotificationManager.IMPORTANCE\u HIGH))
    NotificationCompat.Builder(此,通知ID\u通道ID)
    }否则
    NotificationCompat.Builder(此)
    builder.apply{
    setContentTitle(station.name)
    setContentText(metaToText(meta))
    设置小图标(右可绘制。ic_播放_箭头_白色_24px)
    设置自动取消(错误)
    if(Build.VERSION.SDK\u INT
    正如@Kislingk在注释
    NotificationManager中提到的那样。startServiceInForeground
    已被删除。它被标记为已弃用,并带有

    从提交消息:

    而不是要求提供先验通知,以便 直接将服务启动到前台状态,我们采用两阶段 复合操作,即使是从 后台执行状态。上下文#startForegroundService()不可用 受背景限制,要求 服务通过内部的startForeground()正式进入前台状态 5秒。如果服务不这样做,操作系统和 该应用程序因服务ANR而受到指责

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

    Intent Intent=newintent(这个,MyService.class)
    ContextCompat.startForegroundService(上下文、意图);
    
    服务启动后,使用与以下内容类似的代码创建通知通道:
    @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;
    }
    
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    
        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())
    
    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());
    
    OneTimeWorkRequest work =
         new OneTimeWorkRequest.Builder(MyWorker.class)
     .build();
    WorkManager.getInstance().enqueue(work);    
    
        <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)
        }
    
    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)
    }