原文地址: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. 数组有没有length()方法?String有没有length()方法?

    数组没有length()方法,有length 的属性.String 有length()方法.JavaScript中,获得字符串的长度是通过length属性得到的,这一点容易和Java混淆.

  2. Swing 是线程安全的?

    不是,Swing 不是线程安全的.你不能通过任何线程来更新 Swing 组件,如 JTable.JList 或 JPanel,事实上,它们只能通过 GUI 或 AWT 线程来更新. 这就是为什么 Sw ...

  3. 小技巧之“将Text文件中的数据导入到Excel中,这里空格为分割符为例”

    1.使用场景 将数据以文本导出后,想录入到Excel中,的简便方案, 起因:对于Excel的导出,Text导出明显会更方便些 2.将Text文件中的数据导入到Excel中,这里空格为分割符为例的步骤 ...

  4. 理解feof与EOF

    feof(feof msdn) feof用于判断文件结尾.头文件<cstdio>.使用方法是feof(fp),fp为指向需要判断的文件的指针.如果不到文件结尾,返回0值:如果是文件结尾,返 ...

  5. jq与js的写法,示例回到顶部div滚动显示隐藏

    jq:var top_icon = $('.top_icon')[0]; id写法$('#id'),类写法$('.class'),标签写法$('div') 如:join=document.getEle ...

  6. 移动端比1px还小的border

    巧用border 在移动端 经常出现border,细边框但有的时候 产品大大1px甚至乎会觉得不够细那么要如何写出比1px还要小的border下面是代码 希望对大家有所帮助 .thinner-bord ...

  7. 深入理解ES6之《块级作用域绑定》

    众所周知,js中的var声明存在变量提升机制,因此ESMAScript 6引用了块级作用域来强化对变量生命周期的控制let const 声明不会被提升,有几个需要注意的点1.不能被重复声明 假设作用域 ...

  8. java重载时自动转换咋回事?举例说明

    当一个重载的方法被调用时,Java在调用方法的参数和方法的自变量之间寻找匹配.    但是,这种匹配并不总是精确的.只有在找不到精确匹配时,Java的自动转换才会起作用. (如果定义了test(int ...

  9. CentOS安装图形界面以及eclipse的安装

    图形界面的安装,以GNOME为例: 1.首先运行命令:yum grouplist 会显示可安装的包,可以自己选择安装. 2.运行  yum gruopinstall "GNOME" ...

  10. InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("dbinfo.properties");

    1.与普通程序不同的是,Java程序(class文件)并不是本地的可执行程序.当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class加载到JVM里头运行,负责加载Java ...