设置服务为前台服务。前台服务会在状态栏显示一个通知。通知界面与服务进行关联。
一、什么是通知?
Notification通知是在移动应用APP提供给用户的消息提示,是在移动系统的通知栏中显示。当移动应用不在运行时或者在后台状态下,通过发布通知给用户,用户可以浏览通知的提示信息或者对通知进行某些操作。
图1 通知显示示例
如上图所示:
①小图标:为必要图标,通过setSmallIcon()设置
②应用名称:由系统提供。
③时间戳:由系统提供,可以通过setWhen()进行替换或使用setShowWhen(false)将其隐藏。
④标题:可选内容,通过setContentTitle()设置。
⑤文本:可选内容,通过setContentText()设置。
⑥大图标:可选图标(通常仅用于联系人照片),通过setLargeIcon设置。
⑦样式:通过样式设置大图片。
1.在AndroidManifest.xml中配置发布通知的权限
注意从Android13开始发布通知需要配置android.permission.POST_NOTIFICATIONS许可
<!-- 设置发布通知许可 --> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
2.请求发布通知权限
// 请求发布通知权限
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.TIRAMISU) {
ActivityCompat.requestPermissions(
this,
arrayOf(android.Manifest.permission.POST_NOTIFICATIONS),
0)
}
3. 创建通知渠道和创建通知
(1)创建通知渠道
从 Android 8.0(API 级别 26)开始,所有通知都必须分配到相应的渠道。
//定义通知管理器
val notificationManager:NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
//定义通知渠道的标识
val channelId = "com.example.ch07"
//定义通知渠道的名称
val channelName = "移动应用开发"
//定义通知渠道:指定通知渠道的标识、名称和通知渠道的重要级别
val channel = NotificationChannel(channelId,channelName, NotificationManager.IMPORTANCE_DEFAULT)
//定义通知渠道的描述信息
val channelDesc = "移动应用通知渠道描述"
//创建并配置通知渠道
notificationManager.createNotificationChannel(channel)
(2)创建通知
//创建通知
val notification = Notification.Builder(this,channelId) .apply{
//设置通知标题
setContentTitle("通知实例一")
//设置通知内容
setContentText("欢迎使用通知")
//设置通知时间
setWhen(System.currentTimeMillis())
//设置通知的小图标
setSmallIcon(R.mipmap.nature)
//设置通知的大图标
setLargeIcon(BitmapFactory.decodeResource(resources,R.mipmap.someone))
//设置通知样式
style = Notification.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(resources,R.mipmap.honggutan))
}.build()
(3)发布通知
//创建通知标记
val notificationID = 1
//发布通知到通知栏
notificationManager.notify(notificationID,notification)
4. 通知实例
接下来通过实例来说明发布通知:
(1)定义界面MainScreen
在MainScreen中定义发布通知的按钮,提供交互处理
@Composable
fun MainScreen(postAction:()->Unit){
Box(modifier = Modifier.fillMaxSize(),contentAlignment = Alignment.Center){
TextButton(onClick={
postAction.invoke()
}){
Text("发布通知",fontSize = 24.sp)
}
}
}
(2)定义主活动MainActivity
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//请求权限
requestNotificationPermission()
setContent {
Ch07_DemoTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
MainScreen {
showNotification()
}
}
}
}
}
private fun requestNotificationPermission(){
// 检查通知权限是否已经授予 注意:API 33以上版本需要检查POST_NOTIFICATIONS发布通知权限
if(Build.VERSION.SDK_INT>= Build.VERSION_CODES.TIRAMISU) {
ActivityCompat.requestPermissions(
this,
arrayOf(android.Manifest.permission.POST_NOTIFICATIONS),
0
)
}
}
private fun showNotification(){
//定义通知管理器
val notificationManager:NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
//定义通知渠道的标识
val channelId = "com.example.ch07"
//定义通知渠道的名称
val channelName = "移动应用开发"
//定义通知渠道:指定通知渠道的标识、名称和通知渠道的重要级别
val channel = NotificationChannel(channelId,channelName, NotificationManager.IMPORTANCE_DEFAULT)
//定义通知渠道的描述信息
val channelDesc = "移动应用开发通知渠道描述"
//创建并配置通知渠道
notificationManager.createNotificationChannel(channel)
//创建通知
val notification =
Notification.Builder(this,channelId)
.apply{
//设置通知标题
setContentTitle("通知实例一")
//设置通知内容
setContentText("欢迎使用通知")
//设置通知时间
setWhen(System.currentTimeMillis())
//设置通知的小图标
setSmallIcon(R.mipmap.nature)
//设置通知的大图标
setLargeIcon(BitmapFactory.decodeResource(resources,R.mipmap.someone))
//设置通知样式
style =
Notification.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(resources,R.mipmap.honggutan))
}.build()
//创建通知标记
val notificationID = 1
//发布通知到通知栏,注意从Android13开始发布通知需要配置android.permission.POST_NOTIFICATIONS许可
notificationManager.notify(notificationID,notification)
}
}
运行结果如下所示:
图2 运行结果
运行结果的图片来自网络,如有侵权,告知,会主动删除。
二、前台服务
设置服务为前台服务。前台服务会在状态栏显示一个通知。通知界面与服务进行关联。从Android 14版本(API Level 34)开始,每个前台服务需要声明合适的服务类型。也就是在启动前台服务时,系统会根据服务类型检查是否满足前提条件。
表2-1 常见服务类型
前台服务类型 | 要声明的权限 | 运行时要求前提条件 | 运行时请求权限 | 请求权限调用的方法 | 说明 |
---|---|---|---|---|---|
相机 | FOREGROUND_SERVICE_CAMERA | 请求CAMERA运行时权限 | 在后台访问相机 | ||
连接设备 | FOREGROUND_SERVICE_CONNECTED_DEVICE | CHANGE_NETWORK_STATE或CHANGE_WIFI_STATE或CHANGE_WIFI_MULTICAST_STATE或NFC或TRANSMIT_IR | BLUETOOTH_CONNECT或BLUETOOTH_ADVERTISE或BLUETOOTH_SCAN或UWEB_RANGING | 调用UsbManager.requestPermission() | 与需要的蓝牙、NFC、IR、USB或网络的外部设备互动 |
数据同步 | FOREGROUND_SERVICE_DATA_SYNC | 数据传输的操作 | |||
健康 | FOREGROUND_SERVICE_HEALTH | HIGH_SAMPLING_RATE_SENSORS | BORY_SENSORS或ACTIVITY_RECOGNITION | 为健身类别的应用提供支持 | |
位置 | FOREGROUND_SERVICE_LOCATION | ACESS__COARSE_LOCATION或ACESS_FINE_LOCATION | 为位置信息使用权的长时间运行用例 | ||
媒体 | FOREGROUND_SERVICE_MEDIA_PLAYBACK | 在后台继续播放音频或视频 | |||
媒体投影 | FOREGROUND_SERVICE_MEDIA_PROJECTION | 调用createScreenCaptureIntent() | 使用MediaProjection API将内容投影到外部 | ||
麦克风 | FOREGROUND_SERVICE_MICROPHONE | RECORD_AUDIO | 在后台继续捕获麦克风内容 | ||
致电 | FOREGROUND_SERVICE_PHONE_CALL | MANAGE_OWN_CALLS | 使用COnnetionService API继续当前通话 | ||
远程消息传递 | FOREGROUND_SERVICE_REMOTE_MESSAGING | 将短信从一台设备转移另外一台设备 | |||
配置前台服务 |
启动前台服务
Context.startForeground(int,Notification)
例:创建一个前台服务,播放mp3文件,并在状态栏中显示相应的通知。
1.创建服务
自定义服务,可以控制音频的播放,代码如下:
class MusicService : Service() {
lateinit var mediaPlayer: MediaPlayer
override fun onCreate() {
super.onCreate()
mediaPlayer = MediaPlayer.create(this,R.raw.song3)
}
override fun onBind(intent: Intent): IBinder? {
postNotification()
mediaPlayer.setOnPreparedListener {
mediaPlayer.start()
}
mediaPlayer.setOnCompletionListener {
mediaPlayer.stop()
}
return null
}
override fun onUnbind(intent: Intent?): Boolean {
if(mediaPlayer.isPlaying) {
mediaPlayer.stop()
}
return super.onUnbind(intent)
}
/**
* Request notification permission
* 请求通知权限
*/
private fun requestNotificationPermission(){
val notificationPermissionGranted =
NotificationManagerCompat.from(this).areNotificationsEnabled()
if(!notificationPermissionGranted){
val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
intent.putExtra(Settings.EXTRA_APP_PACKAGE,packageName)
startActivity(intent)
}
}
private fun postNotification(){
//请求通知权限
requestNotificationPermission()
//创建通知管理器
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
//定义通知渠道
val channel = NotificationChannel("music_service","一剪梅",NotificationManager.IMPORTANCE_DEFAULT)
//创建通知渠道
notificationManager.createNotificationChannel(channel)
//定义启动服务的意图
val intent1 = Intent(this,NextActivity::class.java)
//定义悬浮意图
val pendingIntent = PendingIntent.getActivity(this,0,
intent1,PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE)
//创建通知
val notification = NotificationCompat.Builder(this,"music_service").apply{
setOngoing(true)
setOnlyAlertOnce(true)
setContentTitle("播放音乐")
setContentText("正在歌曲播放一剪梅...")
setSmallIcon(R.mipmap.ic_launcher)
setColorized(true)
color = resources.getColor(R.color.teal_200,null)
setContentIntent(pendingIntent)
}.build()
startForeground(1,notification)
}
}
2.在AndroidManifest.xml配置服务
<service
android:foregroundServiceType="mediaPlayback"
android:name=".MusicService"
android:enabled="true"
android:exported="true" />
设置前台服务类型为mediaPlayback
3.在自定义应用中创建通知渠道
class MusicApp: Application() {
override fun onCreate() {
super.onCreate()
//从API 26开始使用通知渠道
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
//定义通知管理器
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
//定义通知渠道的标识
val channelId = "com.example.course.ch07"
//定义通知渠道的名称
val channelName = "移动应用开发"
//定义通知渠道:指定通知渠道的标识、名称和通知渠道的重要级别
val channel = NotificationChannel(
channelId,
channelName,
NotificationManager.IMPORTANCE_DEFAULT)
//创建并配置通知渠道
notificationManager.createNotificationChannel(channel)
}
}
}
需要在AndroidManifest.xml中指定自定义的应用为当前应用,配置自定义应用
<application android:name=".MusicApp" ...> ....... </application>
4. 在主活动MainActivity中启动服务
要启动前台服务需要在AndroidManifest.xml配置前台服务的权限:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
在主活动启动前台服务
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent1 = Intent(this,MusicService::class.java)
requestPermissions()
setContent {
Ch07_DemoTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Column(modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally){
Row{
TextButton(onClick = {
startService(intent1)
}){
Text("播放",fontSize = 20.sp)
}
TextButton(onClick = {
stopService(intent1)
}){
Text("停止",fontSize = 20.sp)
}
}
}
}
}
}
}
private fun requestPermissions(){
//从API 33开始请求发布通知权限
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.TIRAMISU) {
ActivityCompat.requestPermissions(
this,
arrayOf(android.Manifest.permission.POST_NOTIFICATIONS),
0
)
}
}
}
图3
参考文献
-
陈轶 《Android移动应用开发(微课版)》清华大学出版社 2022-9 P203-P238
-
前台服务启动限制
https://developer.android.google.cn/about/versions/12/foreground-services?hl=zh-cn