Android前台服务讲解一
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前台服务讲解一相关推荐
- Android前台服务讲解二之自定义通知视图(RemoteViews)及数据UI更新
Notification支持文字内容显示.震动.三色灯.铃声等多种提示形式,在默认情况下,Notification仅显示消息标题.消息内容.送达时间这3项内容. 1.更新系统通知Notificatio ...
- android 前台服务自定义布局不显示_Android前台服务通知未显示
我正在尝试启动前台服务.我收到通知,该服务确实启动但通知始终被抑制.我仔细检查了应用是否允许在我的设备上的应用信息中显示通知.这是我的代码: private void showNotification ...
- android前台服务需要解绑,Android接入
初始化 配置通道服务 必须在应用启动时(Application的onCreate方法中)初始化通道服务配置,所有进程都需要执行.try { // 参考脚手架demo从配置文件获取 Map ipStra ...
- android 前台服务自定义布局不显示_Android自定义LinearLayout布局显示不完整的解决方法...
发现问题 原需求,在一个伸缩列表中,自定义LinearLayout继承LinearLayout动态添加布局. 然而实现的时候:一共遍历了30条数据,却只显示了一条 断点查看代码:遍历addView() ...
- CVE-2020-0108 安卓前台服务特权提升漏洞
文章目录 前言 正常前台服务 创建流程 实例程序 CVE-2020-0108 漏洞点A 漏洞点B 修复方案 总结 前言 前面一篇文章:Android应用自启动保活手段与安全现状分析 介绍了 Andro ...
- Android开发之如何保证Service不被杀掉(前台服务)
序言 最近项目要实现这样一个效果:运行后,要有一个service始终保持在后台运行,不管用户作出什么操作,都要保证service不被kill.参考了现今各种定制版的系统和安全厂商牛虻软件,如何能保证自 ...
- android8.1启动前台服务,Android - 保活(1)前台服务保活
老婆保佑,代码无BUG 前言 项目中遇到一个需求,需要竟可能的上传用户的定位信息,引发了我对目前已知的保活手段的探究,同时也遇到过客户说,推送不能收到,不能像微信那样,MMP的,不想理客户 目录 一: ...
- android开启前台服务_Android 知识点必知之ANR与OOM
ANR ANR 简介 ANR 全称 (Application Not responding):指的是应用程序未响应,Android 系统对于事件的处理需要在一定时间内完成,如果超过该时间没有得到响应, ...
- Android startService和bindService混合使用、以及前台服务;
Service简单来说就是一个看不见的Activity,在后台默默运行: 可以混合开启Service,无论先startService还是bindService: startService: Inten ...
- android q启动前台服务,Android 启动前台服务,适配 vivo 与 OPPO 手机,第一期
Android 启动前台服务,华为.小米.三星.OPPO.VIVO的配合程度接 这是前文链接,我先甩在这里,上篇文章主要分析了几种品牌手机关于通知的配合程度,这篇文章先写第一期的解决方案,毕竟需求还是 ...
最新文章
- 简单介绍Vue之vue.$set()方法源码案例
- 环境/---Liunx环境安装
- 程序员的认知-中国程序员为什么跳槽
- 常用的机器学习数据挖掘知识点【转】
- 10 字符串相关操作
- Android学习笔记---android平台中利用,SAX解析xml
- 搞AI的产品经理该怎么写PRD?谷歌的导师教你
- fw150um2.0linux驱动下载,fw150um无线网卡驱动
- Docker 方式 MySQL 主从搭建
- 华中科技大学计算机学院离散数学2,华中科技大学计算机学院2015离散数学二考试点评.pdf...
- a5松下驱动器参数设置表_「精品干货」松下A5伺服驱动器参数设置与常见故障解决分析...
- The working copy needs to be upgraded
- java游戏繁体字名字_繁体字游戏名(精选500个)_繁体字游戏名字大全_繁体字游戏网名...
- xshell mysql 权限_使用Xshell连接Linux服务器操作Mysql给Root用户添加远程访问权限
- XSS修炼之独孤九剑——笔记
- iphone6+总显示无服务器,iphone6一直显示无服务为什么啊
- 声纹识别概述(1)初识
- 把VMware虚拟机从一台电脑复制到另一台电脑
- 高分辨率屏电脑 centos虚拟机屏幕使字体变大的方法
- 【ABAQUS仿真问题及解决方案整理】