原文地址:Android8.0 后台服务保活的一种思路 | Stars-One的杂货小窝

项目中有个MQ服务,需要一直连着,接收到消息会发送语音,且手机要在锁屏也要实现此功能

目前是使用广播机制实现,每次MQ收到消息,触发一次启动服务操作逻辑

在Android11版本测试成功,可实现上述功能

步骤

具体流程:

  1. 进入APP
  2. 开启后台服务Service
  3. 后台服务Service开启线程,连接MQ
  4. MQ的消费事件,发送广播
  5. 广播接收器中,处理启动服务(若服务已被关闭)和文本语音播放功能

1.广播注册

<receiver
android:name=".receiver.MyReceiver"
android:enabled="true"
android:exported="true">
</receiver>
public class MyReceiver extends BroadcastReceiver {

    @Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//匹配下之前定义的action
if ("OPEN_SERVICE".equals(action)) {
if (!ServiceUtils.isServiceRunning(MqMsgService.class)) {
Log.e("--test", "服务未启动,先启动服务");
Intent myIntent = new Intent(context, MqMsgService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
} } String text = intent.getStringExtra("text");
Log.e("--test", "广播传的消息"+text); EventBus.getDefault().post(new SpeakEvent(text));
}
}
}

语音初始化的相关操作都在服务中进行的,这里不再赘述(通过EventBus转发时间事件)

这里需要注意的是,Android8.0版本,广播不能直接startService()启动服务,而是要通过startForegroundService()方法,而调用了startForegroundService()方法,则是需要服务在5s内调用一个方法startForeground()

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification notification = NotifyUtil.sendNotification(this, "平板", "后台MQ服务运行中", NotificationCompat.PRIORITY_HIGH);
startForeground(1, notification);
}

上面这段代码,就是写在Service中的onCreate方法内,之前也是找到有资料说,需要有通知栏,服务才不会被Android系统给关闭,也不知道有没有起到作用

还需要注意的是,需要声明权限

<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
NotifyUtil工具类代码
public class NotifyUtil {
private static String channel_id="myChannelId";
private static String channel_name="新消息";
private static String description = "新消息通知";
private static int notifyId = 0;
private static NotificationManager notificationManager; public static void createNotificationChannel(){
if (notificationManager != null) {
return;
}
//Android8.0(API26)以上需要调用下列方法,但低版本由于支持库旧,不支持调用
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel = new NotificationChannel(channel_id,channel_name,importance);
channel.setDescription(description);
notificationManager = (NotificationManager) ActivityUtils.getTopActivity().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(channel);
}else{
notificationManager = (NotificationManager) ActivityUtils.getTopActivity().getSystemService(Context.NOTIFICATION_SERVICE);
}
} public static void sendNotification(String title,String text){
createNotificationChannel();
Notification notification = new NotificationCompat.Builder(ActivityUtils.getTopActivity(),channel_id)
.setContentTitle(title)
.setContentText(text)
.setWhen(System.currentTimeMillis())
.setSmallIcon(ResourceUtils.getMipmapIdByName("ic_launcher"))
.setLargeIcon(BitmapFactory.decodeResource(ActivityUtils.getTopActivity().getResources(), ResourceUtils.getMipmapIdByName("ic_launcher")))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.build();
notificationManager.notify(notifyId++,notification);
} public static Notification sendNotification(Context context,String title,String text,int priority){
createNotificationChannel();
Notification notification = new NotificationCompat.Builder(context,channel_id)
.setContentTitle(title)
.setContentText(text)
.setWhen(System.currentTimeMillis())
.setSmallIcon(ResourceUtils.getMipmapIdByName("ic_launcher"))
.setLargeIcon(BitmapFactory.decodeResource(ActivityUtils.getTopActivity().getResources(), ResourceUtils.getMipmapIdByName("ic_launcher")))
.setPriority(priority)
.build();
notificationManager.notify(notifyId++,notification);
return notification;
} public static void sendNotification(String title, String text, int priority, PendingIntent pendingIntent){
createNotificationChannel();
Notification notification = new NotificationCompat.Builder(ActivityUtils.getTopActivity(),channel_id)
.setContentTitle(title)
.setContentText(text)
.setWhen(System.currentTimeMillis())
.setSmallIcon(ResourceUtils.getMipmapIdByName("ic_launcher"))
.setLargeIcon(BitmapFactory.decodeResource(ActivityUtils.getTopActivity().getResources(), ResourceUtils.getMipmapIdByName("ic_launcher")))
.setPriority(priority)
.setContentIntent(pendingIntent)
.build();
notificationManager.notify(notifyId++,notification);
}
}

2.服务

声明一个服务,然后在服务中开启一个线程,用来连接MQ,MQ的消费事件中,发送广播

//发出一条广播
String ALARM_ACTION_CODE = "OPEN_SERVICE";
Intent intent = new Intent(ALARM_ACTION_CODE);
//适配8.0以上(不然没法发出广播) 显式声明组件
if (DeviceUtils.getSDKVersionCode() > Build.VERSION_CODES.O) {
intent.setComponent(new ComponentName(context, MyReceiver.class));
}
intent.putExtra("text", msg);
context.sendBroadcast(intent);

之后大体上就是测试了,打开APP,然后直接返回桌面,大概1分钟后,APP就无法播放语音

而使用了上述的思路,不管是锁屏还是回到桌面(测试使用的是Android11,谷歌官方系统),都可以实现语音播放,不过未在其他系统的手机上尝试过

原本现场的设备也就是一个华为平板,而且是鸿蒙系统的

Android8.0 后台服务保活的一种思路的更多相关文章

  1. Android P正式版即将到来:后台应用保活、消息推送的真正噩梦

    1.前言 对于广大Android开发者来说,Android O(即Android 8.0)还没玩热,Andriod P(即Andriod 9.0)又要来了.   下图上谷歌官方公布的Android P ...

  2. Android 的保活的两种解决方案

    原文链接:http://blog.csdn.net/pan861190079/article/details/72773549 详细的阐述了 Android 的保活的两种解决方案 —— 由panhao ...

  3. highchart访问一次后台服务返回多张图表数据

    本文承接上一篇,我们制作动态图表的时候,往往需要的不止一张图表,如果每张图表都与服务接口做一次交互的话未免太过频繁,这无论对前后还是后台都是一种压力,本文介绍一种一次访问返回多组数据的方式来减少前台与 ...

  4. 【JavaService】部署Java jar为Windows后台服务

    将Java jar文件部署为Windows后台服务有多种方法:Service Installer.Java service Wrapper.JavaService.exe等等.这里介绍下使用JavaS ...

  5. 答CsdnBlogger问-关于定时和后台服务问题

    本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 前段时间写了不少博客,在问答页面也陆续回答几十个问题,之后Csdn乙同学找到我,说要推荐我参加问答类 ...

  6. Android音视频通话过程中最小化成悬浮框的实现(类似Android8.0画中画效果)

    关于音视频通话过程中最小化成悬浮框这个功能的实现,网络上类似的文章很多,但是好像还没看到解释的较为清晰的,这里因为项目需要实现了这样的一个功能,今天我把它记录下来,一方面为了以后用到便于自己查阅,一方 ...

  7. Android Services (后台服务)

    一.简介 服务是可以在后台执行长时间运行的应用程序组件,它不提供用户界面. 另一个应用程序组件可以启动一个服务,并且即使用户切换到另一个应用程序,它仍然在后台运行. 另外,组件可以绑定到一个服务来与它 ...

  8. Android 后台应用保活、消息推送

    3.针对以往Android版本的各种保活技术回顾 Android P之前为了搞定客户的投诉:“为什么微信能收到消息而你们的IM却不能?”,为了解决这个“痛点”,广大的Android开发者们只能让各种黑 ...

  9. .NET Core 中的通用主机和后台服务

    简介 我们在做项目的时候, 往往要处理一些后台的任务. 一般是两种, 一种是不停的运行,比如消息队列的消费者.另一种是定时任务. 在.NET Framework + Windows环境里, 我们一般会 ...

随机推荐

  1. MySQL_fetch_array 和 MySQL_fetch_object 的区别是 什么?

    以下是 MySQL_fetch_array 和 MySQL_fetch_object 的区别: MySQL_fetch_array() – 将结果行作为关联数组或来自数据库的常规数组返回. MySQL ...

  2. Qunee for HTML5 v1.6新版本发布

    Qunee for HTML5 V1.6正式发布,修复了一些 BUG,增加了滚动条支持,改进了编辑器,增加了JSON 导入导出.告警冒泡.连线流动,UI 定制等扩展示例,欢迎 访问 导航面板 增加了滚 ...

  3. 如何用vue打造一个移动端音乐播放器

    写在前面 没错,这就是慕课网上的那个vue音乐播放器,后台是某音乐播放器的线上接口扒取,虽然这类项目写的人很多,但不得不说这还是个少有的适合vue提升的好项目,做这个项目除了想写一个比较大并且功能复杂 ...

  4. vue简单的父子组件之间传值

     todo-list为例子: 代码: 父传子--------------属性  v-bind 子传父--------------$emit <!DOCTYPE html> <html ...

  5. js多线程worker

    参考地址:https://blog.csdn.net/huang100qi/article/details/89303555?utm_source=app https://www.cnblogs.co ...

  6. Wireshark-过滤器-数据包解析

    目录 过滤器 数据包解析 参考 推荐阅读: https://www.cnblogs.com/zwtblog/tag/计算机网络/ 过滤器 显示过滤器 和 捕获过滤器,俩者使用非常类似. 在Wiresh ...

  7. 超星尔雅看课刷题小tips

    用chrom浏览器,先安装扩展程序Tampermonkey BETA 然后进入> https://greasyfork.org/zh-CN 找一款适合自己的脚本安装即可刷课.

  8. LAN交换机自学习算法

    LAN交换机自学习算法 提示 第二层交完全忽略帧的数据部分协议,仅根据第二层以太网的MAC地址做出转发决策. MAC地址表有时又被称作内容可编址内存(CAM)表 检查源MAC地址 如果源MAC地址不存 ...

  9. 5.Java程序运行机制

    一.编译型和解释型语言区别 计算机是不能理解高级语言的,更不能直接执行高级语言,它只能直接理解机器语言,所以任何的高级语言编写的程序都必须转换成计算机语言,也就是机器码.而这种转换的方式有两种: 编译 ...

  10. css 让一行文字 字与字的隔开一点

     效果: css: letter-spacing: 5rpx; 我这是小程序. 单位自带.px  rem em这些,随自己需要带.