1.服务是什么(Service)

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

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

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

应用场景

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

3.为什么用前台服务

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

4.前台服务使用

4.1定义前台服务

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")}}

执行日志

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;

<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>

注意:

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

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

   companion object{//通知IDprivate const val NOTIFICATION_ID = 1111//唯一的通知通道的IDprivate const val notificationChannelId = "notification_channel_id_01"}/*** 开启前台服务并发送通知*/private fun startForegroundWithNotification(){//8.0及以上注册通知渠道createNotificationChannel()val notification: Notification = createForegroundNotification()//将服务置于启动状态 ,NOTIFICATION_ID指的是创建的通知的IDstartForeground(NOTIFICATION_ID, notification)//发送通知到状态栏val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManagernotificationManager.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_TASKval 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()}

注意事项:

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

创建通知渠道的步骤:

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

/*** 创建通知渠道*/private fun createNotificationChannel(){val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager//唯一的通知通道的IDval 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)}}

importance level主要有七种层次:

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

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

//启动服务
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();}

注意

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

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

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

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

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

4.7停止服务,关闭通知

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

4.8完成整前台服务类

class ForegroundService : Service() {companion object{private const val TAG = "ForegroundService"var serviceIsLive: Boolean = falseprivate const val NOTIFICATION_ID = 1111//唯一的通知通道的IDprivate 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指的是创建的通知的IDstartForeground(NOTIFICATION_ID, notification)//发送通知到状态栏val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManagernotificationManager.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_TASKval 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.总结

  • 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

Android前台服务讲解一相关推荐

  1. Android前台服务讲解二之自定义通知视图(RemoteViews)及数据UI更新

    Notification支持文字内容显示.震动.三色灯.铃声等多种提示形式,在默认情况下,Notification仅显示消息标题.消息内容.送达时间这3项内容. 1.更新系统通知Notificatio ...

  2. android 前台服务自定义布局不显示_Android前台服务通知未显示

    我正在尝试启动前台服务.我收到通知,该服务确实启动但通知始终被抑制.我仔细检查了应用是否允许在我的设备上的应用信息中显示通知.这是我的代码: private void showNotification ...

  3. android前台服务需要解绑,Android接入

    初始化 配置通道服务 必须在应用启动时(Application的onCreate方法中)初始化通道服务配置,所有进程都需要执行.try { // 参考脚手架demo从配置文件获取 Map ipStra ...

  4. android 前台服务自定义布局不显示_Android自定义LinearLayout布局显示不完整的解决方法...

    发现问题 原需求,在一个伸缩列表中,自定义LinearLayout继承LinearLayout动态添加布局. 然而实现的时候:一共遍历了30条数据,却只显示了一条 断点查看代码:遍历addView() ...

  5. CVE-2020-0108 安卓前台服务特权提升漏洞

    文章目录 前言 正常前台服务 创建流程 实例程序 CVE-2020-0108 漏洞点A 漏洞点B 修复方案 总结 前言 前面一篇文章:Android应用自启动保活手段与安全现状分析 介绍了 Andro ...

  6. Android开发之如何保证Service不被杀掉(前台服务)

    序言 最近项目要实现这样一个效果:运行后,要有一个service始终保持在后台运行,不管用户作出什么操作,都要保证service不被kill.参考了现今各种定制版的系统和安全厂商牛虻软件,如何能保证自 ...

  7. android8.1启动前台服务,Android - 保活(1)前台服务保活

    老婆保佑,代码无BUG 前言 项目中遇到一个需求,需要竟可能的上传用户的定位信息,引发了我对目前已知的保活手段的探究,同时也遇到过客户说,推送不能收到,不能像微信那样,MMP的,不想理客户 目录 一: ...

  8. android开启前台服务_Android 知识点必知之ANR与OOM

    ANR ANR 简介 ANR 全称 (Application Not responding):指的是应用程序未响应,Android 系统对于事件的处理需要在一定时间内完成,如果超过该时间没有得到响应, ...

  9. Android startService和bindService混合使用、以及前台服务;

    Service简单来说就是一个看不见的Activity,在后台默默运行: 可以混合开启Service,无论先startService还是bindService: startService: Inten ...

  10. android q启动前台服务,Android 启动前台服务,适配 vivo 与 OPPO 手机,第一期

    Android 启动前台服务,华为.小米.三星.OPPO.VIVO的配合程度接 这是前文链接,我先甩在这里,上篇文章主要分析了几种品牌手机关于通知的配合程度,这篇文章先写第一期的解决方案,毕竟需求还是 ...

最新文章

  1. 简单介绍Vue之vue.$set()方法源码案例
  2. 环境/---Liunx环境安装
  3. 程序员的认知-中国程序员为什么跳槽
  4. 常用的机器学习数据挖掘知识点【转】
  5. 10 字符串相关操作
  6. Android学习笔记---android平台中利用,SAX解析xml
  7. 搞AI的产品经理该怎么写PRD?谷歌的导师教你
  8. fw150um2.0linux驱动下载,fw150um无线网卡驱动
  9. Docker 方式 MySQL 主从搭建
  10. 华中科技大学计算机学院离散数学2,华中科技大学计算机学院2015离散数学二考试点评.pdf...
  11. a5松下驱动器参数设置表_「精品干货」松下A5伺服驱动器参数设置与常见故障解决分析...
  12. The working copy needs to be upgraded
  13. java游戏繁体字名字_繁体字游戏名(精选500个)_繁体字游戏名字大全_繁体字游戏网名...
  14. xshell mysql 权限_使用Xshell连接Linux服务器操作Mysql给Root用户添加远程访问权限
  15. XSS修炼之独孤九剑——笔记
  16. iphone6+总显示无服务器,iphone6一直显示无服务为什么啊
  17. 声纹识别概述(1)初识
  18. 把VMware虚拟机从一台电脑复制到另一台电脑
  19. 高分辨率屏电脑 centos虚拟机屏幕使字体变大的方法
  20. 【ABAQUS仿真问题及解决方案整理】

热门文章

  1. 蚁群背包问题matlab代码,蚁群算法--背包问题
  2. matlabsvd提取特征值_matlab特征值分解和奇异值分解
  3. www.idcnd.net传媒官方客服提供
  4. SAP FI组织结构及概念
  5. 铁路订票系统的简单设计(转)
  6. 数据增强:图片加雾效果实现Python
  7. QQ能上网页打不开解决办法
  8. sap字段及描述底表_SAP财务科目表字段列表说明
  9. MainMenu.xib
  10. 几个在线网页聊天网站