原文地址: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. MyISAM Static 和 MyISAM Dynamic 有什么区别?

    在 MyISAM Static 上的所有字段有固定宽度.动态 MyISAM 表将具有像 TEXT, BLOB 等字段,以适应不同长度的数据类型. MyISAM Static 在受损情况下更容易恢复.

  2. Serlvet 输出中文

    1 response.setHeader("Content-type", "text/html;charset=UTF-8"); 2 response.setC ...

  3. Linux 环境下如何查找哪个线程使用 CPU 最长?

    1.获取项目的 pid,jps 或者 ps -ef | grep java,这个前面有讲过 2.top -H -p pid,顺序不能改变

  4. SDS-redis动态字符串

    与C语言类似,redis自己创建了简单动态字符串SDS(Simple Dynamic String)即简单动态字符串,创建字符串类型的键值对,SDS表示字符串值,键值对的值为字符串对象 SDS用途可以 ...

  5. catkin编译系统

    引言 最近项目中遇到一个需求:将 C++ 程序 (不是 ROS node,只是普通的 C++ 程序)中的变量发布到 ROS topic 上,以便 ROS 中的其他 node 进行后续处理. 原 C++ ...

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

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

  7. [翻译]Service workers:PWA背后的英雄

    原文地址:https://medium.freecodecamp.org/service-workers-the-little-heroes-behind-progressive-web-apps-4 ...

  8. java中Error和Exception用法上有什么区别,Error是怎么回事?

    顺便提一句, 和Exception 相对应的,还有Error,Error(错误)表示系统级的错误和程序不必处理的异常,是JRE(java运行环境)的内部错误或者硬件问题,比如,另外 某一处地方的bug ...

  9. JavaScript实现简单轮播图动画

    运行效果: 源代码: <!DOCTYPE html> <html lang="zh"> <head> <meta charset=&quo ...

  10. 修改django配置文件settings

    默认带数据库sqlite DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join ...