跳转至

Android 启动前台服务

更新日期: 2021-8-26
  • 2021-8-26 创建

前台服务可以给用户提供界面上的操作。 每个前台服务都必须要在通知栏显示一个通知(notification)。用户可以感知到app的前台服务正在运行。 这个通知(notification)默认是不能移除的。服务停止后,通知会被系统移除。 当用户不需要直接操作app,app需要给用户一个状态显示的时候,可以用前台服务。

市面上的app,例如各类音乐app

本文针对Android 8(Oreo,SDK_INT 26)及以后的版本。

使用说明

本例会使用1个Activity和1个Service。演示如何启动前台服务,停止服务。

manifest

在manifest里注册ForegroundDemoActForegroundService1。并且申请权限FOREGROUND_SERVICE

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.rustfisher.tutorial2020">

    <!--  前台服务权限  -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

    <application ...  >

        <service android:name=".service.foreground.ForegroundService1" />

        <activity
            android:name=".service.foreground.ForegroundDemoAct"
            android:launchMode="singleTop" />

    </application>
</manifest>
Activity的启动模式我们选择了singleTop。是为了方便演示点击通知时候的跳转效果。

启动前台服务

在activity中启动服务,调用startForegroundService(Intent)方法。

startForegroundService(Intent(applicationContext, ForegroundService1::class.java))

然后在service中,需要对应地使用startForeground方法。

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    Log.d(TAG, "onStartCommand flags:$flags, startId:$startId [$this] ${Thread.currentThread()}")

    val pendingIntent: PendingIntent =
            Intent(this, ForegroundDemoAct::class.java).let { notificationIntent ->
                PendingIntent.getActivity(this, 0, notificationIntent, 0)
            }

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val chanId = "f-channel"
        val chan = NotificationChannel(chanId, "前台服务channel",
                NotificationManager.IMPORTANCE_NONE)
        chan.lightColor = Color.BLUE
        chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
        val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        service.createNotificationChannel(chan)
        Log.d(TAG, "服务调用startForeground")

        val notification: Notification =
                Notification.Builder(applicationContext, chanId)
                        .setContentTitle("RustFisher前台服务")
                        .setContentText("https://an.rustfisher.com")
                        .setSmallIcon(R.drawable.f_zan_1)
                        .setContentIntent(pendingIntent)
                        .build()
        startForeground(1, notification)
    } else {
        Log.d(TAG, "${Build.VERSION.SDK_INT} < O(API 26) ")
    }
    return super.onStartCommand(intent, flags, startId)
}
我们来看service里的这段代码。创建了一个简单的Notification

  • PendingIntent会被分配给Notification,作为点击通知后的跳转动作
  • 使用NotificationManager先创建了一个NotificationChannel
  • Notification.Builder配置并创建一个Notification,例如配置标题,内容文字,图标等
  • 启动前台服务,调用startForeground(1, notification)方法

在设备上会显示出一个通知(以OnePlus5为例)

点击这个通知,会跳转到ForegroundDemoAct。这是之前用PendingIntent设置的。

停止服务

可以用stopService来停止服务

stopService(Intent(applicationContext, ForegroundService1::class.java))
这样Service退出,走onDestroy方法。

停止前台服务

在Service中调用stopForeground(boolean)方法,能停止前台,但是不退出整个服务。 这个boolean表示是否取消掉前台服务的通知。false表示保留通知。

例如在Service中调用

stopForeground(false)
服务变成了后台服务,并没有退出。此时对应的通知可以滑动取消掉。

报错信息

ANR

在Activity中调用startForegroundService(Intent)启动服务,但是不调用Service.startForeground()。 一加5手机Android10运行log如下

2021-08-26 23:03:25.352 25551-25551/com.rustfisher.tutorial2020 D/rustAppUseStartService: 调用 startForegroundService 主线程信息Thread[main,5,main]
2021-08-26 23:03:25.368 25551-25551/com.rustfisher.tutorial2020 D/rustAppForeground1: onCreate Thread[main,5,main] rustfisher.com
2021-08-26 23:03:25.370 25551-25551/com.rustfisher.tutorial2020 D/rustAppForeground1: onStartCommand flags:0, startId:1 [com.rustfisher.tutorial2020.service.foreground.ForegroundService1@c77d408] Thread[main,5,main]
2021-08-26 23:03:35.375 1596-1720/? W/ActivityManager: Bringing down service while still waiting for start foreground: ServiceRecord{53d70f2 u0 com.rustfisher.tutorial2020/.service.foreground.ForegroundService1}
2021-08-26 23:03:35.382 25551-25551/com.rustfisher.tutorial2020 D/rustAppForeground1: onDestroy [com.rustfisher.tutorial2020.service.foreground.ForegroundService1@c77d408] Thread[main,5,main]

2021-08-26 23:03:52.956 1596-1720/? E/ActivityManager: ANR in com.rustfisher.tutorial2020
    PID: 25551
    Reason: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{53d70f2 u0 com.rustfisher.tutorial2020/.service.foreground.ForegroundService1}

Bad notification

我们在ForegroundService1的方法onStartCommand里加入startForeground。 如果startForeground(0, noti)的id传入0,则会报错RemoteServiceException

 29871-29871/com.rustfisher.tutorial2020 E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.rustfisher.tutorial2020, PID: 29871
    android.app.RemoteServiceException: Bad notification for startForeground

小结

  • 通过startForegroundService()启动前台服务,必须在Service中有startForeground(),否则会ANR,或者崩溃
  • startForeground()中的id不能为0,notification不能为null
  • 服务可以退出前台stopForeground

参考

  • Service综述 https://an.rustfisher.com/android/service/service-intro/
  • Android前台服务 https://developer.android.com/guide/components/foreground-services
本文也发布在

华为云社区

本站说明

一起在知识的海洋里呛水吧。广告内容与本站无关。如果喜欢本站内容,欢迎投喂作者,谢谢支持服务器。如有疑问和建议,欢迎在下方评论~

📖AndroidTutorial 📚AndroidTutorial 🙋反馈问题 🔥最近更新 🍪投喂作者

Ads