android Foreground Service 前台服务/notification全局通知
前言
我在设计一款自用的用于自我管理的app,为了增强自我反馈,我想要这个软件能够持续的显示我执行某一个事件所用的时间。并且为了使得软件不会被系统自动关闭,百般斟酌后我选了前台服务方案。
要素简介
前台服务(Foreground Service)
前台服务(Foreground Service)相比起后台服务(Service),拥有一个优势那就是在系统内存不足的时候不允许系统杀死的服务,并且在运行时需要能被用户所知,需要在状态栏创建一个通知来管理。
全局通知(notification)
全局通知(notification),常用于告知用户某些事件在某个事件发生,在发动时会在状态栏和通知栏都显示信息,用户可以点击信息进行某些功能的管理
功能实现
声明服务与获取权限
声明权限
<!-- 通知权限 -->
<!-- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>-->
<!-- 前台权限权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
声明service
<service android:name=".AppService"/>
在最新版本中notification已被启用,改为使用NotificationCompat
案例如下:
Notification 初始化
registerNotificationChannel();
notifyId = 0x975;//(int) System.currentTimeMillis();
Intent nfIntent = new Intent(this, HostActivity.class);
mBuilder = new NotificationCompat.Builder(this, CHANNEL_ID);
mBuilder.setContentIntent(PendingIntent.
getActivity(this, 0, nfIntent, 0)) // 设置PendingIntent
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(),
R.mipmap.ic_launcher)) // 设置下拉列表中的图标(大图标)
.setContentTitle("下拉列表中的Title") // 设置下拉列表里的标题
.setSmallIcon(R.mipmap.ic_launcher) // 设置状态栏内的小图标
.setContentText("要显示的内容") // 设置上下文内容
.setWhen(System.currentTimeMillis()) // 设置该通知发生的时间
//.setPriority(Notification.PRIORITY_MIN) //设定为最低优先级
.setPriority(Notification.PRIORITY_HIGH) //设定为最高优先级
.setOngoing(true)//设定为点击后不消失
;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
mBuilder.setContentTitle(getResources().getString(R.string.app_name));
}
//notificationManager.notify(notifyId, mBuilder.build());
startForeground(notifyId, mBuilder.build());
- 其中notifyId是通知的唯一标识当通知的notify一致时,再发布通知则会覆盖原有的通知内容。这个方法也常用于实时更新通知内容
- 前台服务发布通知的方法为startForeground,使用方法和notificationManager.notify类似,不需要另外再注册Manager。
NotificationChannel 通知渠道
而在高版本的安卓中,还需要注册通知渠道:
/**
* 注册通知通道
*/
private void registerNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel notificationChannel = notificationManager.getNotificationChannel(CHANNEL_ID);
if (notificationChannel == null) {
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
CHANNEL_NAME,
NotificationManager.IMPORTANCE_HIGH
//NotificationManager.IMPORTANCE_MIN
);
//NotificationManager.IMPORTANCE_HIGH //NONE 为不弹出横幅,HIGH为弹出横幅
;
//是否在桌面icon右上角展示小红点
channel.enableLights(true);
//小红点颜色
channel.setLightColor(Color.RED);
//通知显示
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
//是否在久按桌面图标时显示此渠道的通知
channel.setShowBadge(true);
notificationManager.createNotificationChannel(channel);
}
}
}
update
用于刷新通知内容
/**
* 主自定义线程启动函数
*/
private void startMyThread() {
stopThread=false;
Thread dateFlash=new Thread() {
@Override
public void run() {
super.run();
while (!stopThread) {
try {
Thread.sleep(1000);//每1s刷新一次
} catch (InterruptedException e) {
e.printStackTrace();
}
updateUI();
startForeground(notifyId, mBuilder.build());
}
}
};
dateFlash.start();
}
移除通知
stopForeground(true);
//true表示移除
启动服务
启动前台服务的方法和一般方法相同
/**
* 启动前台服务
*/
private void show2(){
Intent mIntent=new Intent(MainActivity.this,AppService.class) ;
// mIntent.putExtra("startId",now_Event.getEvent_id());
// mIntent.putExtra("startHour",now_Event.getStart_hour());
// mIntent.putExtra("startMin",now_Event.getStart_min());
startService(mIntent);//直接启动服务方式启动
//mAppService.startForeground(110, notification);// 开始前台服务
}
完整AppService.java代码
package com.example.myapplication;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import java.util.Calendar;
import androidx.core.app.NotificationCompat;
public class AppService extends Service {
public static final String CHANNEL_ID = " com.example.myapplication.AppService";
public static final String CHANNEL_NAME = " com.example.myapplication";
@Override
public IBinder onBind(Intent intent) {
return null;
}
private static final String TAG = AppService.class.getSimpleName();
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate()");
registerNotificationChannel();
notifyId = 0x975;//(int) System.currentTimeMillis();
Intent nfIntent = new Intent(this, HostActivity.class);
mBuilder = new NotificationCompat.Builder(this, CHANNEL_ID);
mBuilder.setContentIntent(PendingIntent.
getActivity(this, 0, nfIntent, 0)) // 设置PendingIntent
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(),
R.mipmap.ic_launcher)) // 设置下拉列表中的图标(大图标)
.setContentTitle("下拉列表中的Title") // 设置下拉列表里的标题
.setSmallIcon(R.mipmap.ic_launcher) // 设置状态栏内的小图标
.setContentText("要显示的内容") // 设置上下文内容
.setWhen(System.currentTimeMillis()) // 设置该通知发生的时间
//.setPriority(Notification.PRIORITY_MIN) //设定为最低优先级
.setPriority(Notification.PRIORITY_HIGH) //设定为最低优先级
.setOngoing(true)//设定为点击后不消失
;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
mBuilder.setContentTitle(getResources().getString(R.string.app_name));
}
//notificationManager.notify(notifyId, mBuilder.build());
startForeground(notifyId, mBuilder.build());
startMyThread(); //启动主线程
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy()");
stopForeground(true);
super.onDestroy();
}
/**
* 自定义变量群
*/
boolean stopThread=false;
NotificationCompat.Builder mBuilder;
NotificationManager notificationManager;
int notifyId;
int test_cnt=0;
Calendar calendar = Calendar.getInstance();
/**
* 主自定义线程启动函数
*/
private void startMyThread() {
stopThread=false;
Thread dateFlash=new Thread() {
@Override
public void run() {
super.run();
while (!stopThread) {
try {
Thread.sleep(1000);//每1s刷新一次
} catch (InterruptedException e) {
e.printStackTrace();
}
updateUI();
startForeground(notifyId, mBuilder.build());
}
}
};
dateFlash.start();
}
/**
* 注册通知通道
*/
private void registerNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel notificationChannel = notificationManager.getNotificationChannel(CHANNEL_ID);
if (notificationChannel == null) {
NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
CHANNEL_NAME,
NotificationManager.IMPORTANCE_HIGH
//NotificationManager.IMPORTANCE_MIN
);
//NotificationManager.IMPORTANCE_HIGH //NONE 为不弹出横幅,HIGH为弹出横幅
;
//是否在桌面icon右上角展示小红点
channel.enableLights(true);
//小红点颜色
channel.setLightColor(Color.RED);
//通知显示
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
//是否在久按桌面图标时显示此渠道的通知
channel.setShowBadge(true);
notificationManager.createNotificationChannel(channel);
}
}
}
private void updateUI() {
/**
* 显示当前任务和执行时间
*/
mEvent now_Event = MainActivity.now_Event;
String event_time = "";
//Log.i(TAG, "--------flash--------");
if (now_Event == null)
return;
String event_name = "";
switch (now_Event.getEvent_id()) {
case 10001:
event_name = "休眠";
break;
case 10002:
event_name = "恰饭";
break;
case 10003:
event_name = "其他生理需求";
break;
case 10004:
event_name ="开发项目 ";
break;
case 10005:
event_name ="巩固技术 ";
break;
case 10006:
event_name ="其他工作任务 ";
break;
case 10007:
event_name ="满足求知欲 ";
break;
case 10008:
event_name ="满足好胜心 ";
break;
case 10009:
event_name="休闲娱乐 ";
break;
case 10010:
event_name="学习";
break;
case 10011:
event_name="锻炼";
break;
case 10012:
event_name="自我提升 ";
break;
}
calendar = Calendar.getInstance();
int n_hour = calendar.get(Calendar.HOUR_OF_DAY);
int n_minute = calendar.get(Calendar.MINUTE);
int show_hour;
int show_minute;
int set_hour = now_Event.getStart_hour();
int set_minute = now_Event.getStart_min();
if (n_minute < set_minute) {
show_hour = n_hour - set_hour - 1;
show_minute = n_minute + 60 - set_minute;
} else {
show_hour = n_hour - set_hour;
show_minute = n_minute - set_minute;
}
event_time = ""+show_hour + "小时" + show_minute+"分钟";//记录时间
mBuilder.setContentTitle(event_name); // 设置下拉列表里的标题
mBuilder.setContentText("执行了:"+ event_time);// 设置上下文内容
}
}
效果演示图
某个正在执行的任务:

通知栏状态显示:

完整工程下载(待完成)
结语
完整的APP还在制作中,但是这一部分功能还是成功实现了。
在实现的过程中遇到了一个难题:无法隐藏或者显示横幅
生成通知时会产生一个横幅,有时候又不生成。我期望是不生成的,期望通过app内设置能够实现,但是目前设置均无效,渴望路过的大神能够解惑(〃‘▽’〃)
本文介绍了如何在Android中使用Foreground Service和Notification实现应用在后台持续运行并展示状态栏通知。通过声明服务、获取权限、初始化Notification、创建NotificationChannel、更新及移除通知,确保服务在内存紧张时不会被系统杀死。示例代码和效果演示展示了功能的实现,同时作者在开发过程中遇到横幅显示问题寻求解答。
1000

被折叠的 条评论
为什么被折叠?



