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中…...
QMK Toolbox终极指南:从零开始掌握键盘固件刷写的完整教程
QMK Toolbox终极指南:从零开始掌握键盘固件刷写的完整教程 【免费下载链接】qmk_toolbox A Toolbox companion for QMK Firmware 项目地址: https://gitcode.com/gh_mirrors/qm/qmk_toolbox QMK Toolbox是机械键盘爱好者的必备神器,这款开源工具集…...
二维码修复:3大场景+5步流程,零代码基础也能掌握的受损二维码恢复指南
二维码修复:3大场景5步流程,零代码基础也能掌握的受损二维码恢复指南 【免费下载链接】qrazybox QR Code Analysis and Recovery Toolkit 项目地址: https://gitcode.com/gh_mirrors/qr/qrazybox 日常生活中,我们经常遇到二维码因污渍…...
Phi-4-mini-reasoning保姆级教学:Windows WSL2环境部署全流程
Phi-4-mini-reasoning保姆级教学:Windows WSL2环境部署全流程 1. 模型介绍 Phi-4-mini-reasoning是微软推出的3.8B参数轻量级开源模型,专为数学推理、逻辑推导和多步解题等强逻辑任务设计。这个模型主打"小参数、强推理、长上下文、低延迟"的…...
从零搭建像素剧本工作站:Pixel Script Temple镜像免配置快速上手指南
从零搭建像素剧本工作站:Pixel Script Temple镜像免配置快速上手指南 1. 认识像素剧本圣殿 Pixel Script Temple是一款专为剧本创作者设计的AI辅助工具,它基于强大的Qwen2.5-14B-Instruct模型深度优化,特别适合需要频繁创作剧本、小说或叙事…...
3步让旧款iOS设备重获新生:Legacy-iOS-Kit性能拯救全指南
3步让旧款iOS设备重获新生:Legacy-iOS-Kit性能拯救全指南 【免费下载链接】Legacy-iOS-Kit An all-in-one tool to restore/downgrade, save SHSH blobs, jailbreak legacy iOS devices, and more 项目地址: https://gitcode.com/gh_mirrors/le/Legacy-iOS-Kit …...
es查询是否存在某个字段
1 如果字段就是整个文档json的字段{"query": {"bool": {"must": [{"exists": {"field": "recordUrl"}}]}} }2 如果要查询文档的字段下的子字段,前提是patient是一个objcet,可以涌点访问子属…...
效率倍增:用快马生成openclaw在ubuntu的一键部署与docker化脚本
最近在折腾一个开源项目openclaw的部署,发现每次在Ubuntu服务器上手动安装配置特别费时间。作为一个懒人程序员,我决定研究下怎么把整个流程自动化,结果发现用InsCode(快马)平台可以轻松搞定这件事,效率直接翻倍。 传统部署方式的…...
你还在用for循环清洗CSV?Polars 2.0的scan_csv()+expression DSL已支持自动列式推断与零拷贝转换——立即升级避免被淘汰
第一章:Polars 2.0大规模数据清洗的核心范式变革Polars 2.0 不再将数据清洗视为一系列离散的、命令式的转换操作,而是以“惰性执行图列式语义优先”为基石,重构整个清洗生命周期。其核心变革体现在计算模型、内存管理与API设计三重维度的协同…...
CasRel在企业搜索中的应用:构建结构化语义索引提升召回质量
CasRel在企业搜索中的应用:构建结构化语义索引提升召回质量 1. 引言:当搜索遇到瓶颈 你有没有遇到过这种情况:在公司内部的知识库里搜索“2024年第三季度华东区的销售数据”,结果返回了一堆包含“销售”、“数据”、“华东”等关…...
OneDrive导致桌面图标变白的解决方案
OneDrive导致桌面图标变白的原因主要是由于OneDrive的同步功能或图标缓存损坏。当使用OneDrive的“释放空间”功能时,可能会导致图标变为空白页或默认图标。此外,图标缓存损坏也可能导致图标变白。解决方法:1. 调整OneDrive设置:在…...
