您现在的位置是:首页 >学无止境 >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; } }
参考