Android foreground service explanation one

1. What is the service (Service)

A Service is an application component that performs long-running operations in the background without providing an interface. Services can be started by other app components and will continue to run in the background even if the user switches to another app. Additionally, components can interact with services by binding to them, and even perform inter-process communication (IPC). For example, a service can handle network transactions in the background, play music, perform file I/O, or interact with content providers .

2. What is ForegroundService?

The foreground service performs some actions that the user notices . For example, an audio application uses a foreground service to play audio tracks. The foreground service must display a notification. The foreground service continues to run even if the user stops interacting with the app.

Application scenarios

The most common manifestation is the music playback service . When the application is running in the background, the user can know the current playing content through the notification bar, and perform related operations such as pause, resume, and song cut.

3. Why use front desk service

The system priority of the Service running in the background is relatively low. When the system memory is insufficient, the Service running in the background may be recycled. In order to maintain the normal operation of the background service and related operations, you can choose to set the Service that needs to be kept running as the foreground. Services, so that when the APP is in the background for a long time or is closed (the process is not cleaned up), the service can keep working .

4. Use of front desk services

4.1 Defining foreground services

class ForegroundService : Service() {

    companion object{
        private const val TAG = "ForegroundService"
    }

    override fun onCreate() {
        super.onCreate()
        Log.d(TAG,"OnCreate")
    }

    override fun onBind(intent: Intent?): IBinder? {
        Log.d(TAG,"onBind")
        return null
    }

    
    override fun onUnbind(intent: Intent?): Boolean {
        Log.d(TAG,"onUnbind")
        return super.onUnbind(intent)
    }

    override fun onRebind(intent: Intent?) {
        super.onRebind(intent)
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d(TAG,"onStartCommand")
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "onDestroy")
    }

}

execution log

2022-01-16 09:46:27.430 22461-22461/com.yifan.service D/ForegroundService: OnCreate
2022-01-16 09:46:27.430 22461-22461/com.yifan.service D/ForegroundService: onStartCommand

4.2 Register the service in AndroidManifest.xml

<service android:name=".ForegroundService"  />

To use the foreground service on Android 9 (API level 28) or above , you need to request the FOREGROUND_SERVICE permission, which is the installation permission of FOREGROUND_SERVICE, so the system automatically authorizes the requested APP;

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

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

    <application ...>
        ...
        <service android:name=".ForegroundService"  />
    </application>
</manifest>

Notice:

You need to request the FOREGROUND_SERVICE permission to use the foreground service on Android 9 (API level 28) or above . If the FOREGROUND_SERVICE permission is not requested, the system will throw a SecurityException exception;

4.3 Create service notification content, such as music playing, bluetooth device is connecting, etc.

   companion object{
        //通知ID
        private const val NOTIFICATION_ID = 1111
        //唯一的通知通道的ID
        private const val notificationChannelId = "notification_channel_id_01"
    }

/**
     * 开启前台服务并发送通知
     */
    private fun startForegroundWithNotification(){
        //8.0及以上注册通知渠道
        createNotificationChannel()
        val notification: Notification = createForegroundNotification()
        //将服务置于启动状态 ,NOTIFICATION_ID指的是创建的通知的ID
        startForeground(NOTIFICATION_ID, notification)
        //发送通知到状态栏
        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.notify(NOTIFICATION_ID, notification);
    }


    /**
     * 创建通知渠道
     */
    private fun createNotificationChannel(){
        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        //Android8.0以上的系统,新建消息通道
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            //用户可见的通道名称
            val channelName: String = "Foreground Service Notification"
            //通道的重要程度
            val importance: Int = NotificationManager.IMPORTANCE_HIGH
            //构建通知渠道
            val notificationChannel: NotificationChannel = NotificationChannel(notificationChannelId,
                    channelName, importance)
            notificationChannel.description = "Channel description"
            //LED灯
            notificationChannel.enableLights(true)
            notificationChannel.lightColor = Color.RED
            //震动
            notificationChannel.vibrationPattern = longArrayOf(0,1000,500,1000)
            notificationChannel.enableVibration(true)
            //向系统注册通知渠道,注册后不能改变重要性以及其他通知行为
            notificationManager.createNotificationChannel(notificationChannel)
        }
    }

    /**
     * 创建服务通知
     */
    private fun createForegroundNotification(): Notification {
        val builder: NotificationCompat.Builder = NotificationCompat.Builder(applicationContext, notificationChannelId)
        //通知小图标
        builder.setSmallIcon(R.mipmap.ic_launcher_round)
        //通知标题
        builder.setContentTitle("苏宁窖藏")
        //通知内容
        builder.setContentText("苏宁是国内优秀的跨国企业?")
        //设置通知显示的时间
        builder.setWhen(System.currentTimeMillis())
        //设定启动的内容
        val  activityIntent: Intent = Intent(this, MainActivity::class.java)
        activityIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        val pendingIntent: PendingIntent = PendingIntent.getActivity(this,
                1,activityIntent, PendingIntent.FLAG_UPDATE_CURRENT)
        builder.setContentIntent(pendingIntent)
        //设置通知优先级
        builder.priority = NotificationCompat.PRIORITY_DEFAULT
        //设置为进行中的通知
        builder.setOngoing(true)

        //创建通知并返回
        return builder.build()
    }

Precautions:

Create and manage notification channels. Starting from Android 8.0, you need to create a channel for different types of notifications sent in the United States and China . If a notification is sent on Android 8.0 and above without specifying a notification channel, the notification will not be displayed and will be displayed. record errors;

Steps to create a notification channel:

1) Build a notification channel through a unique channel ID , channel name visible to the user , and notification importance level
; 2) Optionally use setDescription() to specify the relevant description of the notification that the user sees on the system settings page;
3) Register the notification channel through createNotificationChannel() .

/**
     * 创建通知渠道
     */
    private fun createNotificationChannel(){
        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        //唯一的通知通道的ID
        val notificationChannelId = "notification_channel_id_01"

        //Android8.0以上的系统,新建消息通道
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            //用户可见的通道名称
            val channelName: String = "Foreground Service Notification"
            //通道的重要程度
            val importance: Int = NotificationManager.IMPORTANCE_HIGH
            //构建通知渠道
            val notificationChannel: NotificationChannel = NotificationChannel(notificationChannelId,
                    channelName, importance)
            notificationChannel.description = "Channel description"
            //LED灯
            notificationChannel.enableLights(true)
            notificationChannel.lightColor = Color.RED
            //震动
            notificationChannel.vibrationPattern = longArrayOf(0,1000,500,1000)
            notificationChannel.enableVibration(true)
            //向系统注册通知渠道,注册后不能改变重要性以及其他通知行为
            notificationManager.createNotificationChannel(notificationChannel)
        }
    }

There are seven main levels of importance level:

  • IMPORTANCE_DEFAULT : Default notification importance, can be displayed anywhere, with sound.
  • IMPORTANCE_HIGH : Can be displayed anywhere, with sound.
  • IMPORTANCE_LOW : Can be displayed anywhere, no sound.
  • IMPORTANCE_MAX : The highest level of importance, can be displayed anywhere, has a sound, can display notifications on the user's current screen, and can use full screen intents. For example, incoming calls.
  • IMPORTANCE_MIN : No sound, will not appear in the status bar.
  • IMPORTANCE_NONE : will not be displayed anywhere, blocked.
  • IMPORTANCE_UNSPECIFIED : Indicates that the user has no value indicating importance. This value is for persistent preference and should never be associated with an actual notification.

4.4 Called in the Application or where the service needs to be started

//启动服务 
if(!ForegroundService.Companion.serviceIsLive){
            mForegroundService = Intent(this, ForegroundService::class.java)
            mForegroundService.putExtra("Foreground", "This is a foreground service.");
            // Android 8.0使用startForegroundService在前台启动新服务
            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
                startForegroundService(mForegroundService)
            }else{
                startService(mForegroundService)
            }
        }else{
            Toast.makeText(this, "前台服务正在运行中...", Toast.LENGTH_SHORT).show();
        }

Notice

Android 8.0 uses startForegroundService() to start a new service in the foreground

4.5 Stop the service in Application or elsewhere

//停止服务
mForegroundService = Intent(this, ForegroundService::class.java);
stopService(mForegroundService)

4.6 Create a notification when the foreground service is started

ForegroundService
override fun onCreate() {
        super.onCreate()
         //标记服务启动
        serviceIsLive = true
        val notification: Notification = createForegroundNotification()
        //将服务置于启动状态 ,NOTIFICATION_ID指的是创建的通知的ID
        startForeground(NOTIFICATION_ID, notification)
}

4.7 Stop the service and turn off notifications

ForegroundService    
override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "onDestroy")
        stopForeground(true)
        ForegroundService.serviceIsLive = false;
    }

4.8 Complete the whole foreground service class

class ForegroundService : Service() {

    companion object{
        private const val TAG = "ForegroundService"
        var serviceIsLive: Boolean = false
        private const val NOTIFICATION_ID = 1111
        //唯一的通知通道的ID
        private const val notificationChannelId = "notification_channel_id_01"
    }

    override fun onCreate() {
        super.onCreate()
        Log.d(TAG,"OnCreate")
        startForegroundWithNotification()
    }

    override fun onBind(intent: Intent?): IBinder? {
        Log.d(TAG,"onBind")
        return null
    }

    override fun onUnbind(intent: Intent?): Boolean {
        Log.d(TAG,"onUnbind")
        return super.onUnbind(intent)
    }

    override fun onRebind(intent: Intent?) {
        super.onRebind(intent)
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d(TAG,"onStartCommand")
        //数据获取
        val data: String? = intent?.getStringExtra("Foreground") ?: ""
        Toast.makeText(this, data, Toast.LENGTH_SHORT).show()
        return super.onStartCommand(intent, flags, startId)
    }

    /**
     * 开启前景服务并发送通知
     */
    private fun startForegroundWithNotification(){
        //8.0及以上注册通知渠道
        createNotificationChannel()
        val notification: Notification = createForegroundNotification()
        //将服务置于启动状态 ,NOTIFICATION_ID指的是创建的通知的ID
        startForeground(NOTIFICATION_ID, notification)
        //发送通知到状态栏
        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.notify(NOTIFICATION_ID, notification);
    }


    /**
     * 创建通知渠道
     */
    private fun createNotificationChannel(){
        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        //Android8.0以上的系统,新建消息通道
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            //用户可见的通道名称
            val channelName: String = "Foreground Service Notification"
            //通道的重要程度
            val importance: Int = NotificationManager.IMPORTANCE_HIGH
            //构建通知渠道
            val notificationChannel: NotificationChannel = NotificationChannel(notificationChannelId,
                    channelName, importance)
            notificationChannel.description = "Channel description"
            //LED灯
            notificationChannel.enableLights(true)
            notificationChannel.lightColor = Color.RED
            //震动
            notificationChannel.vibrationPattern = longArrayOf(0,1000,500,1000)
            notificationChannel.enableVibration(true)
            //向系统注册通知渠道,注册后不能改变重要性以及其他通知行为
            notificationManager.createNotificationChannel(notificationChannel)
        }
    }

    /**
     * 创建服务通知
     */
    private fun createForegroundNotification(): Notification {
        val builder: NotificationCompat.Builder = NotificationCompat.Builder(applicationContext, notificationChannelId)
        //通知小图标
        builder.setSmallIcon(R.mipmap.ic_launcher_round)
        //通知标题
        builder.setContentTitle("苏宁窖藏")
        //通知内容
        builder.setContentText("苏宁是国内优秀的跨国企业?")
        //设置通知显示的时间
        builder.setWhen(System.currentTimeMillis())
        //设定启动的内容
        val  activityIntent: Intent = Intent(this, MainActivity::class.java)
        activityIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
        val pendingIntent: PendingIntent = PendingIntent.getActivity(this,
                1,activityIntent, PendingIntent.FLAG_UPDATE_CURRENT)
        builder.setContentIntent(pendingIntent)
        builder.priority = NotificationCompat.PRIORITY_DEFAULT
        //设置为进行中的通知
        builder.setOngoing(true)
        //创建通知并返回
        return builder.build()
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "onDestroy")
        stopForeground(true)
        ForegroundService.serviceIsLive = false;
    }

}

5. Summary

  • Android8.0 and above notifications need to add a notification channel, otherwise they cannot be displayed;
  • Android9.0 foreground service notification needs to add <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> permission;
  • Android 8.0 uses startForegroundService to start new services in the foreground;

Android8.0 Background Execution Restrictions
To improve device performance, the system restricts certain behaviors of applications that are not running in the foreground. in particular:

  • Apps running in the background have restricted access to background services
  • Apps fail to register most implicit broadcasts with their manifest

By default, these restrictions apply only to apps targeting O. However, users can enable these restrictions for any application from the Settings screen, even if the application does not target the O platform.

Android8.0 also made the following changes to specific functions:

  • If an app targeting Android 8.0 attempts to use the startService() function without allowing its background service to be created, the function will throw an IllegalStateException.
  • The Context.startForegroundService() function will start a foreground service. Even if the application is running in the background, the system allows it to call Context.startForegroundService(). However, the application must call the startForegroun() function of the new service within 5 seconds after the service is created, otherwise an ANR (Application Not Responding) error will be reported.

refer to:

​​​​​​Android notification bar foreground service - a few circles - Blog Park

Android8.0 uses notifications to create foreground services - Haienzi's blog - CSDN blog - Android to create foreground services

Foreground services  |  Android Developers

Guess you like

Origin blog.csdn.net/ahou2468/article/details/122521035