您现在的位置是:首页 >学无止境 >Android 前台服务网站首页学无止境
Android 前台服务
零、前言
1.服务是什么(Service)
Service 是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。
2.前台服务(ForegroundService)是什么?
前台服务执行一些用户能注意到的操作。例如,音频应用会使用前台服务来播放音频曲目。前台服务必须显示通知。即使用户停止与应用的交互,前台服务仍会继续运行。
应用场景
最常见的表现形式就是音乐播放服务,应用程序后台运行时,用户可以通过通知栏,知道当前播放内容,并进行暂停、继续、切歌等相关操作。
3.为什么用前台服务
后台运行的Service系统优先级相对较低,当系统内存不足时,在后台运行的Service就有可能被回收,为了保持后台服务的正常运行及相关操作,可以选择将需要保持运行的Service设置为前台服务,从而使APP长时间处于后台或者关闭(进程未被清理)时,服务能够保持工作。
4.小结
前台服务可以给用户提供界面上的操作。 每个前台服务都必须要在通知栏显示一个通知(notification)。用户可以感知到app的前台服务正在运行。 这个通知(notification)默认是不能移除的。服务停止后,通知会被系统移除。 当用户不需要直接操作app,app需要给用户一个状态显示的时候,可以用前台服务。
一、创建服务
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Build;
import android.os.IBinder;
import android.text.TextUtils;
import com.zg.fragmentdemo.MainActivity;
import com.zg.fragmentdemo.R;
import com.zg.fragmentdemo.utils.NotificationUtils;
import static android.app.Notification.VISIBILITY_SECRET;
/***
* @Description: 前台服务
* channelId必须要一致,否则会报 android.app.RemoteServiceException: Bad notification for startForeground 错误
* 8.0之上一定要使用 NotificationChannel 适配下才行
* 步骤
* 1.通过 “通知服务” 创建 NotificationChannel
* 2.通过 Notification.Builder 构造器 创建 Notification
* 3.通过 startForeground 开启服务
* 4.高于9.0的版本 manifest需要增加 <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
*
* 在onCreate中创建一个广播接收器,试试能不能接收到 开单或者预约结束后的通知
*/
public class ForeService extends Service {
private MessageReceiver mMsgRecv;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
startForeground(1, getNotification("标题", "内容"));
//注册广播
mMsgRecv = new MessageReceiver();
IntentFilter mFilter = new IntentFilter();
mFilter.addAction(MessageReceiver.MESSAGE_ACTION);
registerReceiver(mMsgRecv, mFilter);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
//取消监听广播
unregisterReceiver(mMsgRecv);
//停止的时候销毁前台服务
stopForeground(true);
}
private Notification getNotification(String title, String message) {
createNotificationChannel();
//创建一个跳转到活动页面的意图
Intent clickIntent = new Intent(this, MainActivity.class);
//clickIntent.putExtra("flag", count);//这里可以传值
//创建一个用于页面跳转的延迟意图
PendingIntent contentIntent = PendingIntent.getActivity(this, 1012, clickIntent
, PendingIntent.FLAG_UPDATE_CURRENT);
//创建一个通知消息的构造器
Notification.Builder builder = new Notification.Builder(this);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
//Android8.0开始必须给每个通知分配对应的渠道
builder = new Notification.Builder(this, "f_channel_id");
}
builder.setContentIntent(contentIntent)//设置内容的点击意图
.setAutoCancel(true)//设置是否允许自动清除
.setSmallIcon(R.mipmap.ic_launcher)//设置状态栏里的小图标
.setTicker("提示消息来啦")//设置状态栏里面的提示文本
.setWhen(System.currentTimeMillis())//设置推送时间,格式为"小时:分钟"
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))//设置通知栏里面的大图标
.setContentTitle(title)//设置通知栏里面的标题文本
.setContentText(message);//设置通知栏里面的内容文本
//根据消息构造器创建一个通知对象
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
Notification notify = builder.build();
return notify;
}
return null;
}
@TargetApi(Build.VERSION_CODES.O)
private void createNotificationChannel() {
NotificationChannel channel = new NotificationChannel("f_channel_id", "CHANNEL_NAME", NotificationManager.IMPORTANCE_HIGH);
//是否绕过请勿打扰模式
channel.canBypassDnd();
//闪光灯
channel.enableLights(true);
//锁屏显示通知
channel.setLockscreenVisibility(VISIBILITY_SECRET);
//闪关灯的灯光颜色
channel.setLightColor(Color.RED);
//桌面launcher的消息角标
channel.canShowBadge();
//是否允许震动
channel.enableVibration(true);
//获取系统通知响铃声音的配置
channel.getAudioAttributes();
//获取通知取到组
channel.getGroup();
//设置可绕过 请勿打扰模式
channel.setBypassDnd(true);
//设置震动模式
channel.setVibrationPattern(new long[]{100, 100, 200});
//是否会有灯光
channel.shouldShowLights();
getManager().createNotificationChannel(channel);
}
private NotificationManager mManager;
private NotificationManager getManager() {
if (mManager == null) {
mManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
return mManager;
}
}
AndroidManifest.xml文件中使用前台服务
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="..."> <!-- 前台服务权限 --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <application ... > <service android:name=".jpush.ForeService" android:enabled="true" android:exported="true" /> <activity android:name=".MainActivity" android:launchMode="singleTask" /> </application> </manifest>
在manifest里注册MainActivity和ForeService。并且申请权限FOREGROUND_SERVICE。
Activity的启动模式我们选择了singleTop。是为了方便演示点击通知时候的跳转效果。
广播接收器
public class MessageReceiver extends BroadcastReceiver {
public static final String MESSAGE_ACTION = "MESSAGE_ACTION";
public static final String MESSAGE_ID = "MESSAGE_ID";
@Override
public void onReceive(Context context, Intent intent) {
if (MESSAGE_ACTION == intent.getAction()) {
String messageid = intent.getStringExtra(MESSAGE_ID);
if (!TextUtils.isEmpty(messageid)) {
new NotificationUtils(context).sendNotification("提示", messageid);
}
}
}
}
二、使用服务
Intent mForegroundService = new Intent(this, ForeService.class);
mForegroundService.putExtra("","")//传值
// Android 8.0使用startForegroundService在前台启动新服务
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
startForegroundService(mForegroundService);
} else {
startService(mForegroundService);
}
ForeService
外面调用服务,在service中,需要对应地使用startForeground方法。
@Override
public void onCreate() {
super.onCreate();
startForeground(1, getNotification("标题", "内容"));
}
三、暂停服务
ForeService
@Override
public void onDestroy() {
super.onDestroy();
//停止的时候销毁前台服务。这里只是 “是否取消掉前台服务的通知”。false表示保留通知。true不保留
stopForeground(true);
//stopForeground(false);//服务变成了后台服务,并没有退出。此时对应的通知可以滑动取消掉。
}
在其他的地方暂停服务
//停止服务,是停止整个服务 mForegroundService = new Intent(this, ForeService.class); stopService(mForegroundService)
四、注意
8.0适配:通知需要加上NotificationChannel,开启前台服务的方式startForegroundService()
9.0适配:manifest.xml文件中需要增加权限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
五、发送广播
同一个手机上另外一个程序使用广播可以推送消息给前台服务
//发送广播,跨进程通信
Intent broadcast = new Intent();
broadcast.setAction("MESSAGE_ACTION");
broadcast.putExtra("MESSAGE_ID",
"您有新的订单");
sendOrderedBroadcast(broadcast, null);
注意:其他的手机上的应用不行。
六、通知工具类
package com.zg.fragmentdemo.utils;
import android.annotation.TargetApi;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Build;
import androidx.core.app.NotificationCompat;
import com.zg.fragmentdemo.MainActivity;
import com.zg.fragmentdemo.R;
import static android.app.Notification.PRIORITY_DEFAULT;
import static android.app.Notification.VISIBILITY_SECRET;
public class NotificationUtils extends ContextWrapper {
public static final String CHANNEL_ID = "default";
private static final String CHANNEL_NAME = "Default Channel";
private static final String CHANNEL_DESCRIPTION = "this is default channel!";
private NotificationManager mManager;
public NotificationUtils(Context base) {
super(base);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel();
}
}
@TargetApi(Build.VERSION_CODES.O)
private void createNotificationChannel() {
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
//是否绕过请勿打扰模式
channel.canBypassDnd();
//闪光灯
channel.enableLights(true);
//锁屏显示通知
channel.setLockscreenVisibility(VISIBILITY_SECRET);
//闪关灯的灯光颜色
channel.setLightColor(Color.RED);
//桌面launcher的消息角标
channel.canShowBadge();
//是否允许震动
channel.enableVibration(true);
//获取系统通知响铃声音的配置
channel.getAudioAttributes();
//获取通知取到组
channel.getGroup();
//设置可绕过 请勿打扰模式
channel.setBypassDnd(true);
//设置震动模式
channel.setVibrationPattern(new long[]{100, 100, 200});
//是否会有灯光
channel.shouldShowLights();
getManager().createNotificationChannel(channel);
}
private NotificationManager getManager() {
if (mManager == null) {
mManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
return mManager;
}
/**
* 发送通知
*/
public void sendNotification(String title, String content) {
NotificationCompat.Builder builder = getNotification(title, content);
getManager().notify(1, builder.build());
}
private NotificationCompat.Builder getNotification(String title, String content) {
NotificationCompat.Builder builder = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID);
} else {
builder = new NotificationCompat.Builder(getApplicationContext());
builder.setPriority(PRIORITY_DEFAULT);
}
//标题
builder.setContentTitle(title);
//文本内容
//builder.setContentText(content);
builder.setStyle(new NotificationCompat.BigTextStyle().bigText(content));
//小图标
builder.setSmallIcon(R.mipmap.logo);
//设置点击信息后自动清除通知
builder.setAutoCancel(true);
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, -1,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pendingIntent);
return builder;
}
/**
* 发送通知
*/
public void sendNotification(int notifyId, String title, String content) {
NotificationCompat.Builder builder = getNotification(title, content);
getManager().notify(notifyId, builder.build());
}
/**
* 发送带有进度的通知
*/
public void sendNotificationProgress(String title, String content, int progress, PendingIntent intent) {
NotificationCompat.Builder builder = getNotificationProgress(title, content, progress, intent);
getManager().notify(0, builder.build());
}
/**
* 获取带有进度的Notification
*/
private NotificationCompat.Builder getNotificationProgress(String title, String content,
int progress, PendingIntent intent) {
NotificationCompat.Builder builder = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID);
} else {
builder = new NotificationCompat.Builder(getApplicationContext());
builder.setPriority(PRIORITY_DEFAULT);
}
//标题
builder.setContentTitle(title);
//文本内容
builder.setContentText(content);
//小图标
builder.setSmallIcon(R.mipmap.ic_launcher);
//设置大图标,未设置时使用小图标代替,拉下通知栏显示的那个图标
//设置大图片 BitmpFactory.decodeResource(Resource res,int id) 根据给定的资源Id解析成位图
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
if (progress > 0 && progress < 100) {
//一种是有进度刻度的(false),一种是循环流动的(true)
//设置为false,表示刻度,设置为true,表示流动
builder.setProgress(100, progress, false);
} else {
//0,0,false,可以将进度条隐藏
builder.setProgress(0, 0, false);
builder.setContentText("下载完成");
}
//设置点击信息后自动清除通知
builder.setAutoCancel(true);
//通知的时间
builder.setWhen(System.currentTimeMillis());
//设置点击信息后的跳转(意图)
builder.setContentIntent(intent);
return builder;
}
}
参考





U8W/U8W-Mini使用与常见问题解决
QT多线程的5种用法,通过使用线程解决UI主界面的耗时操作代码,防止界面卡死。...
stm32使用HAL库配置串口中断收发数据(保姆级教程)
分享几个国内免费的ChatGPT镜像网址(亲测有效)
Allegro16.6差分等长设置及走线总结