Android WebSocket长连接实战:OkHttp心跳与断线重连指南
简介这是一套Android端WebSocket通信的示例工程对应博客《Android端WebSocket基本用法》的配套源码面向需要实现长连接、实时数据交互的移动端开发者重点演示了添加依赖、声明网络权限、自定义WebSocketClient客户端类、建立连接以及发送消息等核心用法。压缩包共124个文件整体仅495KB其中78个xml负责界面布局与工程配置4个kt为源码核心实现配合gradle构建脚本、properties属性文件以及png图标资源结构紧凑便于按层阅读。已有912人学习下载适合刚接触网络协议或准备在Android项目中快速接入WebSocket能力的开发者。通过这份Demo读者能获得一个可直接运行的工程骨架具体看到WebSocketClient如何创建、回调如何组织、与服务器的连接及消息收发如何落地其中的依赖版本、权限设置和客户端封装思路也可直接迁移到实际项目降低自行摸索的成本是一份实用性较强的起步参考。1. 为什么 Android 需要 WebSocket一条长连接把轮询成本降下来App 里做消息推送很多人的第一反应是轮询每隔几秒请求一次接口看起来简单实际在弱网场景下电量和流量的消耗都扛不住。WebSocket 的优势在于它把一次 HTTP 握手升级成一条双向长连接服务端可以在任意时间把数据推过来聊天、行情、设备通知这类实时场景基本都用它。这次拆的是一份 Android 端 WebSocket 简单 Demo它把「连接 → 收发消息 → 断线重连」这条主线完整封装在一个工程里代码量不大但协议层状态回调、回调线程切换、心跳和网络切换这些关键点都覆盖到了。适合用过 OkHttp 但没碰过 WebSocket 的 Android 开发者也适合被 socket 与 HTTP 混合开发绕晕了、想直接拿一份能跑的参考实现的人。2. 技术选型先行OkHttp 与 Java-WebSocket 两条路线的取舍2.1 OkHttp WebSocket内置心跳与拦截器体系OkHttp 自带一个相对完整的 WebSocket 实现它和 HTTP 请求共用同一个 OkHttpClient 里的连接池、Dispatcher 和拦截器链。好处很直接如果项目里上传文件、请求接口已经在用 OkHttp接 WebSocket 就不需要再引一套独立的网络栈线程模型和超时参数沿用原有调优经验长期维护成本最低。我一般会把 WebSocket 的创建封装在业务层而不是直接放在 Activity 里下面这段是 OkHttp 建立连接的最小骨架注释标在回调上方便对照协议状态。OkHttpClient client new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS) // 协议层心跳20 秒发一次 ping .connectTimeout(10, TimeUnit.SECONDS) // 握手超时超过 10 秒视为失败 .readTimeout(0, TimeUnit.SECONDS) // 读超时设 0避免长连接被误杀 .build(); Request request new Request.Builder() .url(ws://192.168.1.100:8080/ws) .build(); WebSocket ws client.newWebSocket(request, new WebSocketListener() { Override public void onOpen(WebSocket webSocket, Response response) { // 101 切换协议成功此时才能安全调用 send() } Override public void onMessage(WebSocket webSocket, String text) { // 收到文本帧text 是一次完整消息无需自己处理分包 } Override public void onClosed(WebSocket webSocket, int code, String reason) { // 双方完成 close 握手后触发code1000 表示正常关闭 } Override public void onFailure(WebSocket webSocket, Throwable t, Response response) { // 握手失败、连接中断、心跳无响应都会走到这里 } });参数有两处要专门说。pingInterval是协议层 ping 帧的发送间隔单位秒常见取值在 15 到 30 之间不要小于 10否则弱网环境下 ping 帧本身被路由丢弃时容易误判连接失效。readTimeout在 WebSocket 场景里建议设 0因为长连接没有普通 HTTP 那种请求响应对齐的边界一旦沿用短连接的 10 秒读超时网络抖动一次连接就被判死。onMessage有两个重载String 版本对应文本帧ByteString 版本对应二进制帧后面封装消息协议时会用到。2.2 Java-WebSocket 轻量库协议层更透传另一条路线是 org.java_websocket 这个独立库不依赖 OkHttp 全家桶。如果项目里只有 WebSocket 这一个网络需求引入它比拖整个 OkHttp 更轻而且它对协议层的暴露更直接——握手响应头、帧类型、关闭码都能在回调里拿到适合想深入理解 WebSocket 细节的开发者。WebSocketClient client new WebSocketClient(new URI(ws://192.168.1.100:8080/ws)) { Override public void onOpen(ServerHandshake handshake) { // handshake.getFieldValue(Sec-WebSocket-Accept) 可查看握手校验值 } Override public void onMessage(String message) { // 文本帧消息二进制帧走另一个 onMessage(ByteBuffer) 重载 } Override public void onClose(int code, String reason, boolean remote) { // remotetrue 表示服务端先发起关闭本地要准备重连 } Override public void onError(Exception ex) { // 连接异常、解码失败都会进入 } }; client.setConnectionLostTimeout(25); client.connect();这里有个关键参数setConnectionLostTimeout它控制连接丢失的判定时间。Java-WebSocket 不提供应用层心跳onClose 的触发依赖 TCP 层的表现遇到典型的网络黑洞场景可能要等 25 秒甚至更久才感知到连接异常。还有一点要记住connect()只是发起异步连接真正握手成功必须等onOpen回调不要在connect()之后立刻调send()。2.3 两个方案的取舍对照表两者不是替代关系而是按项目规模分层的两条路。我整理了一份对照接的时候对着选就行。对比维度OkHttp WebSocketJava-WebSocket依赖体积较大随 OkHttp 一起引入小独立库心跳机制内置 pingInterval需自己实现 ping/pong拦截器体系支持可加日志和鉴权拦截器不适用协议层透明度封装较深高握手细节可见线程模型IO 线程回调需自行切主线程自带线程也需自行切主线程适合场景项目已在用 OkHttp 的长连接服务只用到 WebSocket 的轻量应用或协议学习如果工程里 HTTP 请求已经在走 OkHttp我建议直接选第一条省掉一套线程模型。如果这是一个工具类 App几乎没有别的网络请求第二条路更划算。下面 Demo 的核心逻辑按 OkHttp 路线来写因为实际生产中接触到的踩坑案例更多覆盖人群也更广。3. 搭一个能跑的 Demo连接、收发与生命周期管理3.1 工程配置与依赖引入第一步先把依赖和权限配好。新建 Android 工程后在 app/build.gradle 里加一行依赖注意统一版本避免和其他库的冲突。dependencies { implementation com.squareup.okhttp3:okhttp:4.12.0 }然后是 AndroidManifest.xml网络权限和明文流量开关缺一不可。Demo 默认连的是局域网内的 ws:// 地址属于明文流量Android 9API 28开始默认禁止明文 HTTP/WS只放行 https 和 wss所以要么在 application 标签上开全局开关要么单独配置 network security config。uses-permission android:nameandroid.permission.INTERNET / application android:usesCleartextTraffictrue android:networkSecurityConfigxml/network_security_config ... /application不想全局放开的话可以配置只对调试 IP 放行的策略文件上生产环境时仍然保持默认安全策略。文件放在 res/xml/network_security_config.xml 里。?xml version1.0 encodingutf-8? network-security-config domain-config cleartextTrafficPermittedtrue domain includeSubdomainsfalse192.168.1.100/domain /domain-config /network-security-config两个配置的作用范围要分清cleartextTraffictrue对所有域名放开明文network_security_config只对指定 IP 或域名放开。生产环境里建议用后者全局放开容易在合规审查时被挑出来。3.2 封装一个可复用的 WebSocketManager直接在 Activity 里 new WebSocket 也能跑但回调会分散在各处Activity 销毁之后连接仍然活着很容易泄漏。常见做法是包一个 WebSocketManager 单例让连接的生命周期和 Application 对齐对外只暴露 connect、send、close 三个方法。public class WebSocketManager { private static final WebSocketManager INSTANCE new WebSocketManager(); private OkHttpClient client; private WebSocket webSocket; private final Handler mainHandler new Handler(Looper.getMainLooper()); public interface MessageListener { void onConnected(); void onMessage(String text); void onDisconnected(int code, String reason); } public static WebSocketManager getInstance() { return INSTANCE; } public void connect(String url, MessageListener listener) { client new OkHttpClient.Builder() .pingInterval(20, TimeUnit.SECONDS) .retryOnConnectionFailure(true) .build(); Request request new Request.Builder().url(url).build(); webSocket client.newWebSocket(request, new WebSocketListener() { Override public void onOpen(WebSocket ws, Response response) { mainHandler.post(listener::onConnected); } Override public void onMessage(WebSocket ws, String text) { mainHandler.post(() - listener.onMessage(text)); } Override public void onClosed(WebSocket ws, int code, String reason) { mainHandler.post(() - listener.onDisconnected(code, reason)); } Override public void onFailure(WebSocket ws, Throwable t, Response response) { // 统一走断线回调重连策略由外部决定 mainHandler.post(() - listener.onDisconnected(-1, t.getMessage())); } }); } public boolean send(String text) { if (webSocket null) return false; boolean enqueued webSocket.send(text); if (!enqueued) { // 说明当前连接已不可用需要触发重连 } return enqueued; } public void close(int code, String reason) { if (webSocket ! null) { webSocket.close(code, reason); } } }三段逻辑分别说明。第一connect()里每次重建 OkHttpClient是因为 client 本身持有连接池状态重连场景复用同一个 client 时池里残留的失效 socket 偶尔会造成新连接握手变慢直接重建最稳。第二所有回调通过mainHandler.post切到主线程调用方在回调里直接更新 UI 不会崩。第三send()的返回值表示是否成功入队false 说明 socket 已经写不进去了这时候不要继续发等 onFailure 回调过来再做重连。3.3 消息协议封装JSON 与二进制帧的选择WebSocket 本身只负责把数据按帧搬过去上层用什么格式要双方约定。最省事的方案是 JSON 文本帧可读性好、日志里直接能看、调试成本低。类似聊天这样的 Demo定义一种带 type 字段的消息结构就能覆盖大部分场景。public class ChatMessage { public String type; // 值为 chat / heartbeat / auth public String content; public long timestamp; } // 发送 ChatMessage out new ChatMessage(); out.type chat; out.content hello from android; out.timestamp System.currentTimeMillis(); webSocket.send(new Gson().toJson(out)); // 接收 ChatMessage in new Gson().fromJson(text, ChatMessage.class);文本帧和二进制帧的选择看场景。文本帧适合 JSON、XML、日志这类字符内容抓包和调试直观二进制帧通过onMessage(ByteString bytes)接收适合图片、音频、序列化数据。二进制场景要特别注意字节序大小端和帧头格式必须与服务端逐字段对齐否则解析出来就是一坨乱码。3.4 回调线程与 Android 主线程同步OkHttp 的 WebSocket 回调默认跑在 OkHttp 的 IO 线程池里这些线程不能碰 UI。前面 3.2 已经用 mainHandler 做了切换这里补一个更进阶的做法把收到的消息暴露成 LiveData让 ViewModel 或 Fragment 以观察者方式消费。private final MutableLiveDataString messageLiveData new MutableLiveData(); // 在 onMessage 回调里 mainHandler.post(() - messageLiveData.setValue(text)); // Activity 里 webSocketManager.getMessageLiveData().observe(this, text - { adapter.appendMessage(text); });LiveData 的生命周期管理在这里很顺手页面不可见时不弹 UI恢复时又能立刻拿回最新状态。Demo 阶段用 Handler 足够但消息一旦开始驱动界面变化建议直接换成 LiveData后面会少写很多if (isFinishing())判断。4. 连接稳定性设计心跳参数、重连退避与网络切换4.1 心跳机制与间隔选择WebSocket 协议层有 ping 和 pong 两种控制帧。客户端发 ping服务端必须回 pong这是协议约定的行为。OkHttp 的pingInterval做的就是这件事——每隔 N 秒发一个 ping 帧如果持续收不到 pong连接就被判定死亡并触发 onFailure。间隔设多少要结合实际网络环境。取 15 到 30 秒是业界比较通用的范围太短弱网下 ping 帧本身也可能丢反而造成误判太长中间链路已经超时把连接关了客户端还傻等。服务端判死的超时时间一般设成心跳间隔的 2 到 3 倍两边要配合着定否则会出现客户端还在等 pong服务端早已回收 socket 的情况。还要区分协议层心跳和业务层心跳。协议层 ping/pong 由框架自动发业务层心跳是指在消息体里塞一条{type:heartbeat}消息。如果服务端是自研协议栈对 ping/pong 控制帧支持不完整业务层心跳才是最稳的方案——服务端只要在超时窗口内收到任何有效消息就能判定存活。两种方式可以并存但别把业务层心跳间隔设得和协议层一样密否则 Logcat 里全是心跳日志真正业务消息都被冲走了。4.2 断线重连指数退避与状态机断线重连是长连接 Demo 里最不该省的部分。无脑每 1 秒重连一次服务端一重启几十个客户端请求同时打进直接把服务端打挂。常见做法是指数退避第一次失败等 1 秒第二次 2 秒第三次 4 秒封顶 30 秒同时限制最大重连次数。private static final int MAX_RECONNECT_COUNT 8; private static final long INITIAL_DELAY_MS 1000; private static final long MAX_DELAY_MS 30000; private int reconnectCount 0; private long currentDelayMs INITIAL_DELAY_MS; public void scheduleReconnect() { if (reconnectCount MAX_RECONNECT_COUNT) { // 超过次数后停止自动重连通知业务层人工介入 listener.onDisconnected(-1, reconnect exhausted); return; } reconnectCount; mainHandler.postDelayed(() - connect(currentUrl, listener), currentDelayMs); currentDelayMs Math.min(currentDelayMs * 2, MAX_DELAY_MS); } public void onConnected() { reconnectCount 0; currentDelayMs INITIAL_DELAY_MS; // 连接成功就把退避状态复位 }关键点说明。reconnectCount限制的是单次会话内的连续失败次数防止服务端长时间宕机时客户端每隔几十秒就骚扰一次。currentDelayMs的翻倍逻辑放在 postDelayed 之前实际等待时间按 1s、2s、4s、8s 递增到 30 秒封顶后不再继续翻倍。还有一个容易漏的点重连成功或收到任意有效消息后要把计数器和延时都复位否则下次断线会接着上次的 30 秒开始等体验会很差。4.3 网络切换监听与连接重建另一个需要处理的外部环境变化是网络切换。用户在电梯出来手机从 4G 切到 Wi-Fi或者从 Wi-Fi 切到热点TCP 连接都会中断。Android 上监听网络变化的入口是 ConnectivityManager 的 registerNetworkCallback。ConnectivityManager cm (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkRequest request new NetworkRequest.Builder() .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) .build(); cm.registerNetworkCallback(request, new ConnectivityManager.NetworkCallback() { Override public void onAvailable(Network network) { mainHandler.post(() - { closeQuietly(); // 先关闭旧连接避免僵尸 socket connect(currentUrl, listener); }); } Override public void onLost(Network network) { // 网络全部消失此时不要立刻重连等 onAvailable 再触发 } });最重要的一点网络切换后必须新建 WebSocket不能复用旧连接。旧连接建立在已经失效的网络接口上TCP 层不知道接口已经变更发数据只会一直超时重传。onAvailable的时机是新网络接口已经可用的瞬间在这个回调里关闭旧连接并重新connect是稳妥做法。onLost里不要马上重连因为新网络可能还没就绪此时连接几乎必然失败反而会触发一次无意义的握手。5. 避坑笔记一场 Demo 实测里的五个翻车现场5.1 坑一网络权限与明文流量被拦现象点击连接后日志里立刻抛 SocketException: Permission denied或者握手阶段提示 Cleartext HTTP traffic not permitted。原因AndroidManifest 里漏了 INTERNET 权限或者 Android 9 默认禁止 ws:// 明文流量。这两类问题在 Demo 起步阶段出现频率极高几乎每个人都会撞上一次。解决先在 manifest 里确认权限存在再按 3.1 的两种方式放开明文流量。排查时优先在 Logcat 里搜 blocked 关键字比逐行看代码快很多。5.2 坑二回调线程直接更新 UI现象onMessage 里调用textView.setText()应用直接崩溃Logcat 里是 CalledFromWrongThreadException提示只有创建 View 层级的线程才能触碰 View。原因WebSocket 回调跑在 OkHttp 的 IO 线程池里这些线程不在主线程没有权限操作 UI 组件。我最初写 Demo 时也在这里崩了一次后来才意识到回调线程切换是整个长连接模块里最基础的一环。解决用 Handler 或runOnUiThread切回主线程再更新 UI。我把这条直接写进了 WebSocketManager 的注释里提醒后面接手的人凡是 onOpen、onMessage、onFailure 里要碰 View 的代码都必须先 post 回主线程。5.3 坑三连接断开但客户端毫无感知现象把 App 切到后台再拉回来界面显示仍然在线消息却一条都收不到。原因TCP 连接被中间链路静默回收时双方都不会立刻收到 RST客户端因此残留一个僵尸连接。HTTP 短连接可以通过下一次请求的结果判断是否断开WebSocket 长连接没有这个天然的下一次请求只能靠心跳兜底。解决协议层心跳加业务层超时检测或者利用系统网络回调辅助判断。如果是自己写服务端还可以约定 30 秒内没收到任何数据就主动发 close 帧客户端收到后走 onClosed 重连流程这样断线的感知时间能大幅缩短。5.4 坑四release 包在混淆之后连不上现象debug 包收发正常release 包握手成功但消息发不出去服务端报数据格式错误。原因OkHttp 内部使用了一部分反射和泛型混淆规则缺失时这些类名被改掉消息序列化阶段出错。这类问题很迷惑人因为它不在连接阶段报错而是等到消息解析时才崩。解决在 proguard-rules.pro 里补上 OkHttp 的通用规则。-dontwarn okhttp3.** -keep class okhttp3.** { *; } -dontwarn okio.** -keep class okio.** { *; }如果同时用了 Gson还需要给消息实体类加 keep 规则。凡是出现 debug 正常、release 异常的情况第一反应先检查混淆规则能省掉很大一部分排查时间。5.5 坑五手机熄屏一段时间后连接被系统断开现象早上连好的连接中午回来发现 onFailure 已经被触发但期间没有任何手动操作。原因Android 系统在设备空闲时进入 Doze 模式限制后台网络活动部分机型还在 Wi-Fi 休眠时切到数据网络造成原 TCP 连接失效。这个问题涉及系统省电策略属于环境因素不是代码逻辑能单独解决的。解决业务需要长时间保持连接时通常用前台服务承载连接并配合 PARTIAL_WAKE_LOCK如果只是普通的即时通讯场景可以在业务层做好重连用户唤醒时靠 4.2 的重连逻辑自动恢复。没有银弹选择合理的保活级别才是关键不要为了省电把心跳调到极端频繁。6. 进阶验证技巧用抓包和一套序列确认 Demo 没白写6.1 握手阶段要检查的三个关键头连接建立的过程本质是一次 HTTP Upgrade。抓包工具抓下来的握手响应里重点看三个字段Upgrade 的值必须是 websocketConnection 必须是 UpgradeSec-WebSocket-Accept 则需要用请求里的 Sec-WebSocket-Key 做 base64(SHA1(key GUID)) 校验。第三个字段不用手动算但要在抓包里确认它不是空的。6.2 数据帧与掩码细节握手之后的数据帧客户端发往服务端的帧必须带掩码这是协议强制要求服务端发往客户端的帧不需要掩码。抓包时如果发现客户端发出的帧没有 mask 位说明框架实现有问题服务端大概率会直接断开连接。文本帧 opcode 是 0x1二进制帧是 0x2关闭帧是 0x8。6.3 一套可复现的验证序列项目跑起来后我习惯按下面这套顺序完整验证一遍做完心里才踏实。连上本地服务端确认抓包里有 101 Switching Protocols 响应。从 App 发一条消息服务端日志里收到对应 JSON 内容。从服务端主动推一条消息App 界面实时刷新。把服务端进程杀掉观察 onFailure 回调在预期心跳间隔内触发。重新拉起服务端等指数退避后的下一次重连确认连接自动恢复。连续重复步骤 4 和 5 五次确认退避序列符合预期且没有重复创建连接资源。这套验证本质上做了一次稳定性回归。有一次我在验证第 4 步时发现心跳没有生效查到最后是 pingInterval 的配置被拦截器链里的一个慢日志拦截器拖住了节奏从那以后我每次做长连接改造都会强制完整走一遍这六步同时开着抓包比较心跳帧的实际间隔。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

一维字符数组完全指南:C语言字符串函数与安全操作

一维字符数组完全指南:C语言字符串函数与安全操作

在C语言开发里,一维字符数组大概是接触最频繁、也最容易阴沟翻船的语法点。一个命令行参数、一段日志拼接、一个网络收发的缓冲区,背后都是它。我最早用char name[20]存名字时,直接把用户输入塞进去,结果printf出一串乱码&#xf…

2026/10/11 7:29:50 阅读更多 →
子数组按位或结果计数:利用位运算单调性压缩状态

子数组按位或结果计数:利用位运算单调性压缩状态

1. 题目拆解:从“枚举所有子数组”到“集合压缩”先把这个题目的原型摆出来:给定一个非负整数数组,返回所有子数组的按位或结果中,不同结果的数量。很多朋友第一眼看到“所有子数组”就兴奋了,枚举左端点、右端点、再做…

2026/10/11 7:29:50 阅读更多 →
噪声频谱分析与仿真:从白噪声到量化噪声的工程实践

噪声频谱分析与仿真:从白噪声到量化噪声的工程实践

1. 噪声是信号处理的“底色”——为什么把噪声单独拿出来讲傅里叶变换与频谱分析这个系列,前面几讲我一直在和确定性信号打交道:正弦波、矩形脉冲、调幅信号、扫频信号。这些信号有个共同点——波形能写出确定的数学表达式,频谱图算出来就摆在…

2026/10/11 7:28:50 阅读更多 →

最新新闻

code-review-graph:AI 代码审查的「导航卫星」|SSP Github Daily

code-review-graph:AI 代码审查的「导航卫星」|SSP Github Daily

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 9:08:48 阅读更多 →
【Bug已解决】PyTorch CUDA error: an illegal memory access was encountered 解决方案

【Bug已解决】PyTorch CUDA error: an illegal memory access was encountered 解决方案

【Bug已解决】PyTorch CUDA error: an illegal memory access was encountered 解决方案 问题描述 在 PyTorch 中使用 GPU 进行深度学习训练时,开发者可能会遇到最令人头疼的错误之一: CUDA error: an illegal memory access was encountered这个错误通常…

2026/10/11 9:08:48 阅读更多 →
REA模型:用资源-事件-参与主体重塑企业业务数据建模

REA模型:用资源-事件-参与主体重塑企业业务数据建模

看到“rea”这个标题,我第一反应是把它补全成 REA——Resource-Event-Agent,也就是资源、事件、参与主体。这不是三个单词的简写,而是我做企业信息系统设计和数据建模时绕不开的一套核心方法论。如果你也在和订单表、流水表、明细账表打交道&…

2026/10/11 9:08:48 阅读更多 →
Java线程并发编程实战:协作、线程池与死锁排查全解析

Java线程并发编程实战:协作、线程池与死锁排查全解析

1. 友谊的前提:线程不是玄学,是与你程序“合伙过日子”的一群人“线程间的友谊小船”——我最初看到这个标题时,心里咯噔一下。做了十来年并发编程,最深的体会就是:线程之间的“友谊”确实存在,而且极其容易…

2026/10/11 9:08:48 阅读更多 →
Agent 记忆库实战:用 SQLite 构建持久化对话记忆系统

Agent 记忆库实战:用 SQLite 构建持久化对话记忆系统

1. 从内存到磁盘:为什么 Agent 需要一个真正的记忆库做前端出身的人,对“状态”这个词不会陌生。React 里有 useState、Redux 里有 store、Vue 里有 reactive,我们习惯了把数据放在内存里,页面刷新就重置,组件卸载就销…

2026/10/11 9:08:48 阅读更多 →
Django开源项目运行与代码解读全流程实战指南

Django开源项目运行与代码解读全流程实战指南

上周我交掉了Django的第二次作业,题目是“开源项目运行&解读”。本来以为只是把GitHub上某个项目clone下来、跑通、照着文档念一遍就算完事,结果真正动手才发现,一个开源Django项目从“能打开”到“能讲清楚”,中间隔着一大堆…

2026/10/11 9:07:47 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →