当前位置:   article > 正文

Android前台服务讲解一_android 前台服务

android 前台服务

1.服务是什么(Service)

Service 是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互

2.前台服务(ForegroundService)是什么?

前台服务执行一些用户能注意到的操作。例如,音频应用会使用前台服务来播放音频曲目。前台服务必须显示通知。即使用户停止与应用的交互,前台服务仍会继续运行。

应用场景

最常见的表现形式就是音乐播放服务,应用程序后台运行时,用户可以通过通知栏,知道当前播放内容,并进行暂停、继续、切歌等相关操作。

3.为什么用前台服务

后台运行的Service系统优先级相对较低,当系统内存不足时,在后台运行的Service就有可能被回收,为了保持后台服务的正常运行及相关操作,可以选择将需要保持运行的Service设置为前台服务,从而使APP长时间处于后台或者关闭(进程未被清理)时,服务能够保持工作

4.前台服务使用

4.1定义前台服务

  1. class ForegroundService : Service() {
  2. companion object{
  3. private const val TAG = "ForegroundService"
  4. }
  5. override fun onCreate() {
  6. super.onCreate()
  7. Log.d(TAG,"OnCreate")
  8. }
  9. override fun onBind(intent: Intent?): IBinder? {
  10. Log.d(TAG,"onBind")
  11. return null
  12. }
  13. override fun onUnbind(intent: Intent?): Boolean {
  14. Log.d(TAG,"onUnbind")
  15. return super.onUnbind(intent)
  16. }
  17. override fun onRebind(intent: Intent?) {
  18. super.onRebind(intent)
  19. }
  20. override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
  21. Log.d(TAG,"onStartCommand")
  22. return super.onStartCommand(intent, flags, startId)
  23. }
  24. override fun onDestroy() {
  25. super.onDestroy()
  26. Log.d(TAG, "onDestroy")
  27. }
  28. }

执行日志

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在AndroidManifest.xml注册服务

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

需要在Android 9(API级别28)或者以上使用前台服务需要请求FOREGROUND_SERVICE权限,FOREGROUND_SERVICE这个安装权限,因此系统自动授权给请求的APP;

  1. <manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
  2. <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
  3. <application ...>
  4. ...
  5. <service android:name=".ForegroundService" />
  6. </application>
  7. </manifest>

注意:

需要在Android 9(API级别28)或者以上使用前台服务需要请求FOREGROUND_SERVICE权限,若没有请求FOREGROUND_SERVICE权限,系统会抛出SecurityException异常;

4.3创建服务通知内容,例如音乐播放,蓝牙设备正在连接等

  1. companion object{
  2. //通知ID
  3. private const val NOTIFICATION_ID = 1111
  4. //唯一的通知通道的ID
  5. private const val notificationChannelId = "notification_channel_id_01"
  6. }
  7. /**
  8. * 开启前台服务并发送通知
  9. */
  10. private fun startForegroundWithNotification(){
  11. //8.0及以上注册通知渠道
  12. createNotificationChannel()
  13. val notification: Notification = createForegroundNotification()
  14. //将服务置于启动状态 ,NOTIFICATION_ID指的是创建的通知的ID
  15. startForeground(NOTIFICATION_ID, notification)
  16. //发送通知到状态栏
  17. val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
  18. notificationManager.notify(NOTIFICATION_ID, notification);
  19. }
  20. /**
  21. * 创建通知渠道
  22. */
  23. private fun createNotificationChannel(){
  24. val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
  25. //Android8.0以上的系统,新建消息通道
  26. if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
  27. //用户可见的通道名称
  28. val channelName: String = "Foreground Service Notification"
  29. //通道的重要程度
  30. val importance: Int = NotificationManager.IMPORTANCE_HIGH
  31. //构建通知渠道
  32. val notificationChannel: NotificationChannel = NotificationChannel(notificationChannelId,
  33. channelName, importance)
  34. notificationChannel.description = "Channel description"
  35. //LED灯
  36. notificationChannel.enableLights(true)
  37. notificationChannel.lightColor = Color.RED
  38. //震动
  39. notificationChannel.vibrationPattern = longArrayOf(0,1000,500,1000)
  40. notificationChannel.enableVibration(true)
  41. //向系统注册通知渠道,注册后不能改变重要性以及其他通知行为
  42. notificationManager.createNotificationChannel(notificationChannel)
  43. }
  44. }
  45. /**
  46. * 创建服务通知
  47. */
  48. private fun createForegroundNotification(): Notification {
  49. val builder: NotificationCompat.Builder = NotificationCompat.Builder(applicationContext, notificationChannelId)
  50. //通知小图标
  51. builder.setSmallIcon(R.mipmap.ic_launcher_round)
  52. //通知标题
  53. builder.setContentTitle("苏宁窖藏")
  54. //通知内容
  55. builder.setContentText("苏宁是国内优秀的跨国企业?")
  56. //设置通知显示的时间
  57. builder.setWhen(System.currentTimeMillis())
  58. //设定启动的内容
  59. val activityIntent: Intent = Intent(this, MainActivity::class.java)
  60. activityIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
  61. val pendingIntent: PendingIntent = PendingIntent.getActivity(this,
  62. 1,activityIntent, PendingIntent.FLAG_UPDATE_CURRENT)
  63. builder.setContentIntent(pendingIntent)
  64. //设置通知优先级
  65. builder.priority = NotificationCompat.PRIORITY_DEFAULT
  66. //设置为进行中的通知
  67. builder.setOngoing(true)
  68. //创建通知并返回
  69. return builder.build()
  70. }

注意事项:

创建和管理通知渠道,从Android8.0开始,需要为发送的美中不同类型的通知创建一个渠道,如果在Android8.0及以上在未指定通知频道的情况下发送通知,通知不会显示,会记录错误;

创建通知渠道的步骤:

1)通过一个唯一的channel ID ,对用户可见的channel name通知重要程度importance level构建一个通知渠道;
2)可选的使用setDescription()指定用户在系统设置页面看到的关于通知的相关描述;
3)通过createNotificationChannel()注册通知渠道。

  1. /**
  2. * 创建通知渠道
  3. */
  4. private fun createNotificationChannel(){
  5. val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
  6. //唯一的通知通道的ID
  7. val notificationChannelId = "notification_channel_id_01"
  8. //Android8.0以上的系统,新建消息通道
  9. if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
  10. //用户可见的通道名称
  11. val channelName: String = "Foreground Service Notification"
  12. //通道的重要程度
  13. val importance: Int = NotificationManager.IMPORTANCE_HIGH
  14. //构建通知渠道
  15. val notificationChannel: NotificationChannel = NotificationChannel(notificationChannelId,
  16. channelName, importance)
  17. notificationChannel.description = "Channel description"
  18. //LED灯
  19. notificationChannel.enableLights(true)
  20. notificationChannel.lightColor = Color.RED
  21. //震动
  22. notificationChannel.vibrationPattern = longArrayOf(0,1000,500,1000)
  23. notificationChannel.enableVibration(true)
  24. //向系统注册通知渠道,注册后不能改变重要性以及其他通知行为
  25. notificationManager.createNotificationChannel(notificationChannel)
  26. }
  27. }

importance level主要有七种层次:

  • IMPORTANCE_DEFAULT: 默认notification importance,可以在任何地方显示,有声音。
  • IMPORTANCE_HIGH:可以在任何地方显示,有声音.
  • IMPORTANCE_LOW:可以在任何地方显示,没有声音.
  • IMPORTANCE_MAX:重要程度最高,可以在任何地方显示,有声音,可以在用户当前屏幕上显示通知,可以使用full screen intents.比如来电。
  • IMPORTANCE_MIN:无声音,不会出现在状态栏中。
  • IMPORTANCE_NONE:在任何地方都不会显示,被阻塞。
  • IMPORTANCE_UNSPECIFIED:表示用户没有表示重要性的值。这个值是为了持久的首选项,并且永远不应该与实际的通知相关联。

4.4在Application或需要开启服务的地方调用

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

注意

Android 8.0使用startForegroundService()在前台启动新服务

4.5在Application或其他地方停止服务

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

4.6启动前台服务时创建通知

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

4.7停止服务,关闭通知

  1. ForegroundService
  2. override fun onDestroy() {
  3. super.onDestroy()
  4. Log.d(TAG, "onDestroy")
  5. stopForeground(true)
  6. ForegroundService.serviceIsLive = false;
  7. }

4.8完成整前台服务类

  1. class ForegroundService : Service() {
  2. companion object{
  3. private const val TAG = "ForegroundService"
  4. var serviceIsLive: Boolean = false
  5. private const val NOTIFICATION_ID = 1111
  6. //唯一的通知通道的ID
  7. private const val notificationChannelId = "notification_channel_id_01"
  8. }
  9. override fun onCreate() {
  10. super.onCreate()
  11. Log.d(TAG,"OnCreate")
  12. startForegroundWithNotification()
  13. }
  14. override fun onBind(intent: Intent?): IBinder? {
  15. Log.d(TAG,"onBind")
  16. return null
  17. }
  18. override fun onUnbind(intent: Intent?): Boolean {
  19. Log.d(TAG,"onUnbind")
  20. return super.onUnbind(intent)
  21. }
  22. override fun onRebind(intent: Intent?) {
  23. super.onRebind(intent)
  24. }
  25. override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
  26. Log.d(TAG,"onStartCommand")
  27. //数据获取
  28. val data: String? = intent?.getStringExtra("Foreground") ?: ""
  29. Toast.makeText(this, data, Toast.LENGTH_SHORT).show()
  30. return super.onStartCommand(intent, flags, startId)
  31. }
  32. /**
  33. * 开启前景服务并发送通知
  34. */
  35. private fun startForegroundWithNotification(){
  36. //8.0及以上注册通知渠道
  37. createNotificationChannel()
  38. val notification: Notification = createForegroundNotification()
  39. //将服务置于启动状态 ,NOTIFICATION_ID指的是创建的通知的ID
  40. startForeground(NOTIFICATION_ID, notification)
  41. //发送通知到状态栏
  42. val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
  43. notificationManager.notify(NOTIFICATION_ID, notification);
  44. }
  45. /**
  46. * 创建通知渠道
  47. */
  48. private fun createNotificationChannel(){
  49. val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
  50. //Android8.0以上的系统,新建消息通道
  51. if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
  52. //用户可见的通道名称
  53. val channelName: String = "Foreground Service Notification"
  54. //通道的重要程度
  55. val importance: Int = NotificationManager.IMPORTANCE_HIGH
  56. //构建通知渠道
  57. val notificationChannel: NotificationChannel = NotificationChannel(notificationChannelId,
  58. channelName, importance)
  59. notificationChannel.description = "Channel description"
  60. //LED灯
  61. notificationChannel.enableLights(true)
  62. notificationChannel.lightColor = Color.RED
  63. //震动
  64. notificationChannel.vibrationPattern = longArrayOf(0,1000,500,1000)
  65. notificationChannel.enableVibration(true)
  66. //向系统注册通知渠道,注册后不能改变重要性以及其他通知行为
  67. notificationManager.createNotificationChannel(notificationChannel)
  68. }
  69. }
  70. /**
  71. * 创建服务通知
  72. */
  73. private fun createForegroundNotification(): Notification {
  74. val builder: NotificationCompat.Builder = NotificationCompat.Builder(applicationContext, notificationChannelId)
  75. //通知小图标
  76. builder.setSmallIcon(R.mipmap.ic_launcher_round)
  77. //通知标题
  78. builder.setContentTitle("苏宁窖藏")
  79. //通知内容
  80. builder.setContentText("苏宁是国内优秀的跨国企业?")
  81. //设置通知显示的时间
  82. builder.setWhen(System.currentTimeMillis())
  83. //设定启动的内容
  84. val activityIntent: Intent = Intent(this, MainActivity::class.java)
  85. activityIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
  86. val pendingIntent: PendingIntent = PendingIntent.getActivity(this,
  87. 1,activityIntent, PendingIntent.FLAG_UPDATE_CURRENT)
  88. builder.setContentIntent(pendingIntent)
  89. builder.priority = NotificationCompat.PRIORITY_DEFAULT
  90. //设置为进行中的通知
  91. builder.setOngoing(true)
  92. //创建通知并返回
  93. return builder.build()
  94. }
  95. override fun onDestroy() {
  96. super.onDestroy()
  97. Log.d(TAG, "onDestroy")
  98. stopForeground(true)
  99. ForegroundService.serviceIsLive = false;
  100. }
  101. }

5.总结

  • Android8.0及以上通知需要添加通知渠道,否则无法显示;
  • Android9.0前台服务通知需要添加<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>权限;
  • Android 8.0使用startForegroundService在前台启动新服务;

Android8.0后台执行限制
为提高设备性能,系统会限制未在前台运行的应用的某些行为。具体而言:

  • 在后台运行的应用对后台服务的访问受到限制
  • 应用无法使用其清单注册大部分隐式广播

默认情况下,这些限制仅适用于针对O的应用。不过用户可以从Settings屏幕为任意应用启用这些限制,即使应用并不是以O为目标平台。

Android8.0还对特定函数做出了如下变更:

  • 如果针对Android8.0的应用尝试在不允许创建其后台服务的情况下使用startService()函数,则该函数将引发一个IllegalStateException.
  • Context.startForegroundService()函数将启动一个前台服务。即使应用在后台运行,系统也允许其调用Context.startForegroundService().不过,应用必须在创建服务后的5秒内调用改服务的startForegroun()函数,否则将报ANR(Application Not Responding)错误。

参考:

​​​​​​Android通知栏前台服务 - 几圈年轮 - 博客园

Android8.0使用通知创建前台服务_Haienzi的博客-CSDN博客_android 创建前台服务

Foreground services  |  Android Developers

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/464444
推荐阅读
相关标签
  

闽ICP备14008679号