Android:使用Service处理息屏后的WebSocket的服务端推送消息并传递给前端
前言
之前我们在使 RESTful 访问服务端时,一般都是客户端请求服务端应答的方式,这种通讯方式,对于需要持续获取数据的情形都是采用轮询的方式,但是这种方式对两边的性能消耗很大,特别是服务端的压力很大。现在当我们使用WebSocket时,这类问题迎刃而解了。服务端可以根据需要向客户端实时推送消息。
WebSocket的协议很简单,客户端的onMessage事件可以很方便的接收消息。我们可以收到后传递给前端的Activity处理更新UI.
现代手机为了省电,屏幕在很短的时候就关闭了,但有时候我们不希望屏幕一灭,应用也跟着睡着了,手机息屏后,我们仍然希望用户能通过语音给用户传递信息。就像你切换屏幕了,高德能继续给你语音导航一样。这种情况单独使用Activity是无能为力的,Android系统设计为只要屏幕一灭,任何Activity的UI活动都会睡眠。不过,息屏后,后台的WebSocket客户端是能够继续接受消息的,但是没办法传到前端的活动了。这时候我们需要引入Android的Service来处理。
在讲解正文之前我们先回顾一下WebSocket的基本使用。
WebSocket与回调接口
在之前的文章中我们介绍了WebSocket的基本使用。就是利用OkHttp3的WebSocket相关功能搭建自己的调用程序框架。这里为了适应Service的使用,我们对WebSocket程序稍微加以改造一下。
首先我们将静态方法改造为实例方法,并使用单例模式。
其次,我们引入一个回调接口,这个很关键。
WebSocketListenerCallback
public interface WebSocketListenerCallback {void onDataReceived(String action, String data);void onFailure(String text);
}
它包含2个方法,
onDataReceived 处理收到消息后怎么办
onFailure 处理通讯失败
我们的WebSocket程序设计为可以设定多个Callback, 在onMessage 收到消息后,逐个执行每个Callback的onDataReceived方法。整个WebSocket程序的代码如下。使用前需要引入Okhttp3库。
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;import com.bob.app.C;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;public class WSClient {private static WSClient instance;private WebSocket socket;private final List<WebSocketListenerCallback> callbacks = new ArrayList<>();private final OkHttpClient client;private WSClient() {OkHttpClient.Builder cb = new OkHttpClient.Builder();cb.readTimeout(C.SOCKET_TIMEOUT, TimeUnit.SECONDS);cb.connectTimeout(C.SOCKET_TIMEOUT, TimeUnit.SECONDS);cb.writeTimeout(C.SOCKET_TIMEOUT, TimeUnit.SECONDS);client = cb.build();}public static synchronized WSClient getInstance() {if (instance == null) {instance = new WSClient();}return instance;}public synchronized void init() {Request request = new Request.Builder().url(C.serverUrl).build();WSListener listener = new WSListener();socket = client.newWebSocket(request, listener);}public void addCallback(WebSocketListenerCallback callback) {if (!callbacks.contains(callback)) {callbacks.add(callback);}}public void removeCallback(WebSocketListenerCallback callback) {callbacks.remove(callback);}public synchronized void send(String action, Object data) {long txNo = System.currentTimeMillis();String message = action + "$$" + txNo + "$$" + U.toJSONString(data);if (socket != null) {socket.send(message);}}public void disconnect(int code, String reason) {if (socket != null) {socket.close(code, reason);socket = null;}}private class WSListener extends WebSocketListener {@Overridepublic void onMessage(@NonNull WebSocket webSocket, @NonNull String text) {for (WebSocketListenerCallback callback : callbacks) {String[] str = text.split("\$\$");String action = str[0];//操作指令,String message = str.length > 2 ? str[2] : "";//信息正文callback.onDataReceived(action, message);}}@Overridepublic void onFailure(@NonNull WebSocket webSocket, @NonNull Throwable t, @Nullable Response response) {for (WebSocketListenerCallback callback : callbacks) {callback.onFailure("Error: " + t.getMessage());//错误信息传出去}}}
}
C是一个常数类,里面有些常数,比如SOCKET_TIMEOUT = 5;
Service接收推送消息
关于Service的基本使用,网上有很多资料。我们这里通过实际的场景应用来消化理解。
我们现在的应用场景就是:收到后台消息后,通过Service传递给前端应用,然后语音播报。
首先自己的Service继承基类,并且需要实现回调方法WebSocketListenerCallback
在初始化WebSocket的时候我们就把Callback传进去。并初始化。
整个Service的完整代码如下:
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;import com.bob.app.common.WSClient;
import com.bob.app.common.WebSocketListenerCallback;public class ActiveDataService extends Service implements WebSocketListenerCallback {@Overridepublic void onCreate() {super.onCreate();WSClient wsClient = WSClient.getInstance();//获取实例wsClient.addCallback(this);//传入CallbackwsClient.init();//执行初始化}public void onDataReceived(String action, String str) {if (A.ACTIVE_DATA.equals(action)) {//如果指令是推送新消息Intent intent = new Intent(BC.BROADCAST_ACTIVE_DATA);//收到最新实时消息intent.putExtra("data", str);LocalBroadcastManager.getInstance(this).sendBroadcast(intent);}}public void onFailure(String str) {Intent intent = new Intent(BC.BROADCAST_SERVER_OFFLINE);//服务端访问异常LocalBroadcastManager.getInstance(this).sendBroadcast(intent);}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {startForeground(1, createNotification());return START_STICKY;}private Notification createNotification() {NotificationChannel channel = new NotificationChannel("channel", "Service", NotificationManager.IMPORTANCE_LOW);NotificationManager manager = getSystemService(NotificationManager.class);if (manager != null) {manager.createNotificationChannel(channel);}NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "channel").setContentTitle(getString(R.string.app_name)).setContentText(getString(R.string.fetching_data)).setSmallIcon(android.R.drawable.ic_notification_overlay).setAutoCancel(true).setOngoing(false).setPriority(NotificationCompat.PRIORITY_DEFAULT); // Make sure the priority is LOWreturn builder.build();}@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onDestroy() {super.onDestroy();}
}
其中BC.BROADCAST_ACTIVE_DATA和BC.BROADCAST_SERVER_OFFLINE是2个常数字符串,可以自己取名字。
在Android里面,由于Activity和Service的生命周期并不相同,Service息屏后还能活动,因此Service并不能直接引用Activity也不能直接与Activity交互,只能通过广播间接交互。意思就是Service发送广播,Activity可以订阅后处理。上面的代码展示了怎样发送广播。
createNotification是一个常规方法,创建一个通知信息栏。这个会让用户知道一个后台服务在干嘛。有的手机会显示比如华为等,有的手机也默认不显示,比如Oppo等
Activity接收Service广播信息
Service发送的广播,Activity可以通过订阅的方式来使用。
具体的订阅方法如下:
private final BroadcastReceiver receiver1 = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String data = intent.getStringExtra("data");finishGetActiveData(data);//接收到最新数据,传递到处理方法}};private final BroadcastReceiver receiver2 = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {showLogin();//接收到网络异常,退回到登录界面}};
先声明BroadcastReceiver, receiver的目的就是声明收到数据后怎么办。在onReceive方法里接收数据,然后传递到实现方法里面。这里我们声明了2个receiver,一个是收到正常的最新信息后怎么办。另一个是收到网络错误信息后怎么办。
然后在onCreate方法里注册receiver
Intent serviceIntent = new Intent(this, ActiveDataService.class);
ContextCompat.startForegroundService(this, serviceIntent);
LocalBroadcastManager.getInstance(this).registerReceiver(receiver1, new IntentFilter(BC.BROADCAST_ACTIVE_DATA));
LocalBroadcastManager.getInstance(this).registerReceiver(receiver2, new IntentFilter(BC.BROADCAST_SERVER_OFFLINE));
注意其中的 IntentFilter的用法,一般一个receiver只过滤接收一种消息。
这样我们就完成把消息传送给Activity这个数据链路了。
然后我们可以在finishGetActiveData方法里面干该干的事情了,比如调用TTS语音播报啥的。
看到这里,有的小伙伴可能会问,这个Callback我能不能在Activity里将WebSocket初始化,然后传入Callback呢,答案是可以的,可以将Activity实现WebSocketListenerCallback后传入WebSocket,也是一样的效果,还更简单,不用广播。不过前面我们讲过,这种直接使用Activity初始化WebSocket的程序只能在亮屏时功能正常,息屏了程序就跟着睡眠了。
另外,需要强调的一点时,要实现息屏后服务正常,需要手机在设置里关闭省电功能,并且允许应用后台活动。否则写了也白搭。只有用户才有最终决定权。
总结
要想息屏后,部分手机功能正常接收后台推送消息并传递给Activity处理,需要做如下处理:
- WebSocket引入回调接口并在onMesssge中调用。
- 创建一个Service实现回调接口,在回调方法中发送广播
- Activity中声明接收广播的receiver,并实现处理receiver收到的的数据。
相关文章:
Android:使用Service处理息屏后的WebSocket的服务端推送消息并传递给前端
前言 之前我们在使 RESTful 访问服务端时,一般都是客户端请求服务端应答的方式,这种通讯方式,对于需要持续获取数据的情形都是采用轮询的方式,但是这种方式对两边的性能消耗很大,特别是服务端的压力很大。现在当我们使…...

Git Bash Here 中文显示乱码的处理方法
在使用"open Git Bash Here"时,遇到中文显示乱码问题。 原因:通常是由于编码设置不正确导致的。 open Git Bash Here —>鼠标右击空白处,点击「选项」|或「Options」 在「文本」或 「Text」选项卡中,找到"local…...
FreeBSD安装教程
FreeBSD 是一个功能强大且可靠的开源 UNIX 操作系统,适合服务器和桌面环境。本文将介绍如何安装 FreeBSD,从系统准备到基础设置,为你快速上手提供帮助。 一、准备工作 1. 硬件要求 CPU:支持 x86 或 AMD64 架构的处理器。 内存&a…...

Loki 各模式简介
目录 Loki 部署模式 单片模式 简单可扩展 微服务模式 Loki 部署模式 Loki 是一个由许多微服务组成的分布式系统。它还具有独特的构建模型,其中所有这些微服务都存在于同一个二进制文件中。 您可以使用命令行标志配置单个二进制文件的行为-target,以指…...

MySQL八股-全局锁,表级锁,表锁,元数据锁,意向锁,行级锁,行锁,间隙锁,临键
文章目录 全局锁表级锁表锁(表级锁)元数据锁(MDL,Meta Data Lock,表级锁)元数据锁演示元数据锁兼容的情况元数据锁互相阻塞的情况 意向锁(Intention lock,表级锁)意向锁分类意向锁演示:意向共享锁(**IS**)与…...

(四)Spring Cloud Alibaba 2023.x:高效构建 Gateway 网关服务
目录 前言 准备 项目集成 pom.xml引入依赖 启动类 yml文件添加网关配置 修改消费者FeignService类 结果验证 前言 Spring cloud alibaba 体系中构建微服务,我们使用Spring Cloud Gateway 作为服务网关, Gateway是Spring 官方推出的一款基于 Web…...

Android XR 是什么?解释它的功能、设备、开发工具等
什么是“Android XR”? Android XR是最新配备AI的OS(操作系统),兼容耳机和眼镜(AR眼镜)。 沉浸式剧场 从视频列表中选择... 您可以体验完全身临其境的视频观看体验。 无限工作空间 您的现实世界将成为您…...

【算法】实体关系抽取
✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢,在这里我会分享我的知识和经验。&am…...

Codeforces Round 993 (Div. 4)题解
A. Easy Problem 思路:经过看了一眼,我们发现,ab的和一定是n,且两个都是正整数, 所以a的范围就是从1~n-1 所以输出n-1即可 #include<bits/stdc.h> using namespace std; #define int long long int t; int n…...

【计算机网络】期末考试预习复习|中
作业讲解 转发器、网桥、路由器和网关(4-6) 作为中间设备,转发器、网桥、路由器和网关有何区别? (1) 物理层使用的中间设备叫做转发器(repeater)。 (2) 数据链路层使用的中间设备叫做网桥或桥接器(bridge)。 (3) 网络层使用的中间设备叫做路…...

从零用java实现 小红书 springboot vue uniapp (4)个人主页优化
前言 移动端演示 http://8.146.211.120:8081/#/ 前面的文章我们基本完成了详情页开发 今天我们具体的去进行实现个人中心 并且分享我开发时遇到的问题 首先先看效果 我们对布局整体规划一下 个人名片 半透明背景 刚开始我用的是 <view style"background-image: ur…...

为“行车大脑”降温:Simdroid-EC助力汽车ECU设计研发
ECU(Electronic Control Unit,电子控制单元)被誉为汽车的行车大脑,在工作时会产生大量的热量,而其散热存在以下难题:一是工作环境恶劣,ECU常处于高温环境中;二是ECU所处的空间较为狭…...

视频汇聚平台:Liveweb视频流媒体平台视频监控系统解决方案
数字化技术在安防领域的广泛应用已经成为公安等重要执法部门的重要趋势,主要得益于无线网络通信技术和计算机技术的快速进步。传统的视频监控系统存在诸多局限,例如只能进行现场监视,报警信息传输简单,无法远距离传输视频信号&…...

通过解调使用正则化相位跟踪技术进行相位解包裹
1. 绪论 光学计量学通常使用光学干涉仪来测量各种物理量。1,2 根据应用的不同,可以使用多种类型的干涉仪,但它们的共同目标是产生一个由被测物理量调制的条纹图案。使用这种光束编码程序可以检测到的物理量范围非常广:深度测量、应变分析、温…...
VMware替代 | 双一流大学采用ZStack ZSphere虚拟化平台加速医学应用算法分析
某双一流大学医学部在面对日益增长的医学应用算法分析需求时,选择采用ZStack ZSphere虚拟化平台,以满足其高性能计算和GPU业务应用的迫切需求。该平台凭借其轻量化、卓越性能及易用性,成功解决了医学部在虚拟化及GPU应用场景中的挑战。随着平…...

UNIAPP框架uView初步集成与开发设计
uView UI,是uni-app生态最优秀的UI框架,全面的组件和便捷的工具会让您信手拈来,如鱼得水。本文章分享UNIAPP集成使用uView页面动态开发设计。 一、使用HBuilder X 直接导入插件,下载后重启 uView - DCloud 插件市场 二、配置样…...
C05S08-LVS负载均衡
一、LVS 1. LVS概述 LVS(Linux Virtual Server、Linux虚拟服务)是一种基于Linux系统集群的负载均衡方案,属于四层的负载均衡。 集群:将相同组件部署在不同的服务器上,提供统一的服务,以及同样的功能&…...

C 语言代码诗韵:数字功能的雅集华章
函数基本操作练习 主要内容: 本任务主要练习函数的申请、定义、调用等,主要包含以下功能: 1)编写函数,输入一个整数,求各个数字之和; 2)编写函数,计算1!2&…...

ps案例制作
宣传海报 暖色调海报商品展示图...

【C++】列表初始化、声明、范围for、array容器
列表初始化、声明、范围for、array容器 一、统一的列表初始化1.1 使用{ }初始化1.2 initializer_list容器 二、声明2.1 auto关键字2.2 decltype关键字2.3 nullptr关键字 三、范围for四、array容器和forward_list容器 一、统一的列表初始化 1.1 使用{ }初始化 在C98中…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...

Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...

Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...