- 音视频
- 直播
- 移动开发
【免费下载链接】pure_live
纯粹直播:哔哩哔哩/虎牙/斗鱼/快手/抖音/网易cc/YY直播/Twitch直播/SOOP直播/M38自定义源应有尽有。
pure_live 是一款开源的多平台直播聚合播放器,v2.6.0 阶段稳定版(2.6.0+4076)围绕"真实合并上游、修复公开问题、收敛 CPU/GPU/内存热点、全平台串行构建"四条主线完成了一次完整的稳定性迭代。本文基于 v2.6.0 阶段稳定版、近期 Issue 审计、依赖与接口审计 与 构建与发布,结合仓库源码梳理该版本的问题根因、修复实现、依赖策略、质量门禁与五平台产物,帮助读者理解这套"维护分支稳定性优先"的发布方法论。
版本快照与上游基线
| 项目 | 值 |
|---|---|
| 版本号 | 2.6.0+4076 |
| 维护仓库 | liuchuancong/pure_live |
| 上游基线 | liuchuancong/pure_live@c3ae29bb |
| 发布日期 | 2026-08-23 |
| 最终整合源码 | b1d61b27(门禁)、56275967(五平台最终构建树) |
本阶段的核心动作是真实合并上游至c3ae29bb,涉及关注刷新、房间详情补全、版本历史容错、弹幕模板、BetterPlayer 尺寸与 Windows 小窗设置等改动;当上游实现与维护分支的稳定性目标存在语义冲突时,按"维护版稳定性优先"的原则整合,而不是机械照搬。
本阶段范围
1. 上游合并与冲突语义整合
上游c3ae29bb带来的功能面覆盖:
- 关注刷新:关注页手动/自动刷新链路;
- 房间详情补全:链接占位房间进入后补齐元数据;
- 版本历史容错:版本更新历史读取的异常兜底;
- 弹幕模板:弹幕样式模板体系;
- BetterPlayer 尺寸与 Windows 小窗设置:播放器尺寸策略与桌面小窗配置。
这些改动在合并时逐一按维护版稳定性目标复评,冲突语义以"不破坏既有用户行为"为底线。
2. 公开 Issue 修复
本轮修复了近期公开问题#765、#769、#770、#771、#773,并吸收#767的高 DPI 性能边界经验,同时覆盖#774(v2.6.0 关注页手动刷新失效)的回归修复。各问题根因与处理详见下文"稳定性设计"。
3. 性能热点收敛
针对首页图片缓存反复驱逐、非 Exo 原生纹理初始化环、画面比例切换重开流三处 CPU/GPU/内存热点做了专项修复,见"稳定性设计"中的对应条目。
4. 依赖更新策略
更新全部可直接升级且与 Flutter 3.47 兼容的依赖;保留有明确类型或原生组合约束的固定项。完整策略见"依赖审计"。
5. 分阶段串行构建
Android、Windows、Linux、macOS、iOS 五平台分阶段串行构建,不向上游提交 PR,产物清单见"串行构建与产物"。
稳定性设计
异步竞态:以"房间号 + 平台 + 加载代次"为提交条件
房间详情、醒目留言(Super Chat)、播放地址与弹幕连接等异步任务,统一以**当前"房间号 + 平台 + 加载代次"(load generation)**为提交条件。迟到响应只结束自身任务,不再覆盖新房间的状态——这是"切房隔离"的基础。
在 直播控制器 及各类弹幕适配器(如 斗鱼弹幕、虎牙弹幕)中都可以看到 generation/epoch 机制的运用,其思路与关注刷新控制器中的_refreshEpoch一脉相承(见下文)。
播放会话复用:PiP、横竖屏、后台恢复
PiP、横竖屏切换、后台恢复继续复用同一播放器和弹幕会话;恢复 revision 只重建展示订阅,只有连接异常时才触发重连。这避免了"转一圈就重开流"的经典问题,也呼应了 #767 修复中"画面适配模式更新只改变布局,不重新拉流"的原则。
有界队列与显式释放
弹幕历史、待处理批次、文本/表情缓存与本地互动队列全部保持有界;直播控制器关闭时统一取消 Worker、Timer、订阅和子控制器。从 关注刷新控制器 的onClose()可以直观看到这一纪律:_refreshEpoch++使所有在途刷新失效,随后依次移除生命周期观察者、取消 Tab 监听、释放全部订阅、取消三类定时器并 dispose 全部 Worker。
图片缓存:按可见尺寸解码、刷新不逐卡驱逐
图片按可见尺寸解码并复用磁盘缓存;刷新只推进全局 epoch,不逐卡删除同一缓存条目。
在 房间卡片封面实现 中:
final cacheWidth = (logicalWidth * MediaQuery.devicePixelRatioOf(context)).round().clamp(240, 720).toInt(); return CachedNetworkImage( imageUrl: coverUrl, cacheKey: epoch == 0 ? coverUrl : '$coverUrl#$epoch', cacheManager: AppImageCacheManager.instance, fit: BoxFit.cover, memCacheWidth: cacheWidth, // 按可见尺寸解码,控制内存占用 useOldImageOnUrlChange: true, // 刷新时保留旧图,避免占位符闪屏 fadeInDuration: Duration.zero, fadeOutDuration: Duration.zero, ... );关键点在于:cacheKey只随全局 epoch 变化,同一轮刷新不会反复销毁重建每个卡片的图片元素;useOldImageOnUrlChange: true配合memCacheWidth消除了"刷新即闪白/占位符交替"和整网格 CPU 尖峰。面积卡片(area_card.dart)与播放页弹窗(play_other.dart)采用同一模式。
Windows 原生视频视图与画面适配
Windows 原生视频视图受可见视口约束;播放器适配模式更新只改变布局,不重新拉流。这是对 #767(Windows 4K/高 DPI GPU 偏高)的核心修复:此前视频原生视图按源尺寸经FittedBox放大/裁切,窗口变化与呈现更新可能重开数据源;现在非 Exo 输出按可见视口和applyBoxFit约束渲染,画面比例切换仅改呈现 revision。
Windows 小窗:几何独立持久化与多显示器校正
这是 #765 的完整修复,也是本轮 Windows 侧最复杂的稳定性设计:
- 小窗位置、大小与主窗口几何独立保存;
- 提供记忆开关(默认开启)与显式重置入口;
- 默认跟随应用所在显示器;多显示器变化时把旧矩形校正到仍可见的工作区(显示器被移除后回到应用所在屏)。
实现集中在 Windows 小窗驱动:
final DisplayAwarePipWindow windowsPipWindow = DisplayAwarePipWindow( workAreasReader: _readWorkAreas, // 读取全部显示器可见工作区 readSavedBounds: _readSavedBounds, // 仅在开启记忆开关时读取 writeSavedBounds: _writeSavedBounds, // 移动/缩放后写入(横竖屏分桶) alwaysOnTop: () => SettingsService.to.player.windowsPipAlwaysOnTop.value, normalMinSize: const Size(WindowSizeController.minWindowWidth, WindowSizeController.minWindowHeight), );_readWorkAreas()通过screenRetriever.getAllDisplays()枚举各显示器,以visiblePosition/visibleSize构建工作区矩形;无显示器时回退到主显示器;_readSavedBounds()只有在rememberPipPosition为真时才返回记忆的几何,并且横屏/竖屏视频分别持久化(windowsPipShowsPortraitVideo依据驱动内实际视频尺寸判断,避免录像借用直播的朝向记忆);- 设置页提供完整的操作面:小窗记忆开关、基准尺寸(200–720 px)、最小宽(100–320 px)、最小高(60–240 px)、自由宽高比、始终置顶与位置重置,其中
_syncPipConfig()会把尺寸策略实时推入驱动,_changePipAlwaysOnTop()采用"先应用、失败回滚、再提交状态"的事务式更新。
关注刷新:#774 的根因与修复
Issue #774(v2.6.0 关注页手动刷新失效、未开播仍显示)的根因链条在 近期 Issue 审计 中被拆得很清楚:
- 下拉刷新头不可触发:每个平台页虽然重新包了
EasyRefresh,但子列表又显式安装了 AndroidClampingScrollPhysics,边界位移先被外层物理模型消耗,刷新头只有回调配置而没有可触发的拖动链路; - 生命周期刷新互相取消:冷启动核验与 450 ms 后的 resumed 刷新通过
_refreshEpoch互相取消,失败房间继续沿用上次进程的"开播位"; - 网络异常被伪装成成功:部分平台把网络异常伪装成"已下播"结果。
对应修复:
- 改用
EasyRefresh.builder,并把其物理模型直接交给当前平台的唯一纵向列表。在 关注页网格 中有明确注释:EasyRefresh 必须拥有安装在其纵向子列表上的确切 physics,否则 Android 外层ClampingScrollPhysics会先消耗边界位移,导致"回调存在但下拉动画永远无法触发"。
return EasyRefresh.builder( key: ValueKey('favorite_pull_to_refresh_$siteId'), header: MaterialHeader( triggerOffset: 72, triggerWhenRelease: true, clamping: true, ), onRefresh: onRefresh, childBuilder: childBuilder, // physics 由 EasyRefresh 注入唯一列表 );项目为此还维护了自定义滚动物理模型 pure_live_scroll_physics.dart:iOS/macOS 保留BouncingScrollPhysics原生弹簧,其余平台用ClampingScrollPhysics,导航条/分页视图等有限选择器则用PureLiveBoundedScrollPhysics硬性钳制首尾边界(#769 修复)。
- 所有刷新经单事务锁串行。在 关注刷新控制器 中,
_refreshLock.synchronized保证同一时刻只有一个刷新事务拥有快照写入权:
// One refresh owns the snapshot transaction at a time. The former epoch // scheme cancelled whichever pass happened to finish second; a lifecycle // resume 450 ms after launch could therefore discard startup verification // and leave failed rooms with yesterday's live bit. final pending = _refreshLock.synchronized<void>(() async { final refreshEpoch = _refreshEpoch; ... });启动/恢复/手动触发绕过失败冷却(bypassFailureCooldown),失败房间进入 5 分钟冷却(_refreshFailureRetryAfter),失败汇总单行输出(summarizeFavoriteRefreshFailures最多列 6 条,避免刷屏)。
- 启动期间发布"状态未知但位置稳定"的非持久预览:冷启动先按持久化的缓存元数据分桶(在线/录播/离线),但把每个状态位标为 unknown(
isVerifyingFavorites),避免陈旧"开播位"误导,也避免 clear/reorder/重现造成的视觉抖动;CC、Twitch、SOOP 使用严格元数据刷新路径,虎牙 REPLAY 进入录播分组。
关注按钮:#773 的响应式修复
Issue #773(链接跳转及普通入口无法关注)的根因是:按钮缓存局部布尔值并依赖 EventBus 刷新;链接占位房间详情补全后身份变化,旧局部状态与持久列表脱节。修复为:按钮直接观察收藏列表并用"平台 + 房间号"复合身份判断,详情未完成时显示加载态,跳转等待导航完成。核心判断在 收藏房间控制器:
bool isFavorite(LiveRoom liveroom) { return favoriteRooms.v.any((candidate) => candidate.hasSameIdentity(liveroom)); }即关注状态由持久列表实时推导,而不是按钮自己缓存一份可能过期的布尔值。
醒目留言(Super Chat)边界
实时 WebSocket 醒目留言已进入统一消息列表并自动过期;SC 页面直接观察 RxList 实时快照,并按房间代次隔离。在 SuperChatPage 中可以看到它使用独立的primary: false+ClampingScrollPhysics列表,避免继承外层 PrimaryScrollController 导致越界空白(#769 同类问题)。已知边界:斗鱼"进入直播间前已发送且仍有效"的历史醒目留言缺少已验证的稳定公开回补入口,本轮只保证进入后的实时 SC、自动过期与切房隔离。
依赖审计
Dart/Flutter 依赖
scrollview_observer 1.27.1为本轮唯一可直接升级项;dynamic_color 1.9.0为当前 Flutter Material API 的兼容线,2.x 主题类型迁移另立任务(后续版本已完成迁移,见 DEPENDENCY_AUDIT.md 中dynamic_color 2.1.0与material_ui桥接)。
Android 工具链
| 组件 | 版本 |
|---|---|
| AGP | 9.3.1 |
| Gradle | 9.5.0 |
| Google Services | 4.5.0 |
| compileSdk/targetSdk | 37 |
| 字节码目标 | JDK 17 |
版本组合遵循 AGP 9.3 官方默认基线;Git、本地路径和 Native Assets 依赖继续锁定完整提交或仓库快照,发布构建使用 lockfile(flutter pub get --enforce-lockfile)。
质量门禁
最终上游整合源码b1d61b27通过本地完整门禁,结果如下:
- Flutter Analyze:0 issue(耗时 323.8 秒);
- 单元与 Widget 测试:224/224 通过;
- 公开接口探测:26/26 通过,覆盖 Bilibili、Douyu、Huya、Kuaishou、Douyin、网易 CC、Twitch 与 SOOP Live;
- Built-in Kotlin、构建策略、设备 UI 地图、锁文件与变更文件格式检查全部通过;
- 完整门禁耗时557.633 秒;重型进程峰值内存 5.97 GiB、峰值 CPU 17.27%;结束后活跃重型进程为 0;
- 记录文件:
local-artifacts/build-records/20260822T193705756Z-quality-full.json。
这里的"公开接口探测"对应仓库中的 接口探测脚本,后续版本已扩展为 42 项覆盖更多平台;质量门禁工具链在 BUILD_AND_RELEASE.md 有完整说明(tool/local_ci.ps1 -Scope Full)。
串行构建与产物
各平台严格按Android → Windows → Linux → macOS → iOS串行执行。最终同步上游c3ae29bb后涉及公共播放器代码,因此五个平台均从同一最终代码树56275967重新构建。Android、Linux、macOS 与 iOS 的工作流每轮只启用一个目标平台,Windows 使用本机 Release 构建。此前 iOS 首轮暴露的 Xcode 26 隐式引擎 registrar 可选值错误已由609e8345修复并纳入最终构建。
| 平台 | 产物 | SHA-256 | 来源/记录 |
|---|---|---|---|
| Android arm64-v8a | PureLive-2.6.0-4076-arm64-v8a-release.apk | 704f7a873905b2d8d89f66b347a128562f6ea0c2ac9f7e9421ef12c4dd230e77 | 56275967;正式 RSA-4096 签名,包名com.mystyle.purelive,仅含arm64-v8a |
| Windows x64 | PureLive-2.6.0-windows-x64-setup.exe | 693a8dba51ca825780309098605c3622724c467e37d23d472847aa35cee3559c | 56275967;本机 Inno Setup,可选安装目录 |
| Windows x64 | PureLive-2.6.0-4076-windows-x64-portable.zip | b08258dbf747c7aaae5030669d63a1653f60abb251c95de920403db2c51a28b0 | 56275967;20260822T195209357Z-build-windowsx64-release.json |
| Linux x64 | PureLive-2.6.0-4076-linux-x64.tar.gz | 4ba6a34c7f7d0d67b244af9d269f4ffe07185953ff4e4f73e57820402bb6b3d5 | 56275967 |
| macOS Universal | PureLive-2.6.0-4076-macos-universal.dmg | 073026d33e50515b8f1ca68fddba609403ba445378924b5a28d2bfd9cfd7380d | 56275967 |
| macOS Universal | PureLive-2.6.0-4076-macos-universal.zip | c6508e80e82b5286a1491b7b0ed09a5cb2d509efe2ed32fc4b40f6358ba1328c | 同上 |
| iOS arm64 | PureLive-2.6.0-4076-ios-arm64-unsigned-app.zip | e5c888f248da2b3cb9c8f10343494b9e91fcc4246f611fdd862dadbf04349b45 | 56275967 |
| iOS TrollStore | PureLive-2.6.0-4076-ios-arm64-trollstore.ipa | 099756d50f7f82cb01dd5db202f59b56099628bdba6b98960c496d461dd9f564 | 同上;工作流完成临时签名与 IPA 结构验证 |
Android 使用apksigner复核 v2 签名、版本名和 ABI;全部远端产物侧车校验通过,Linux 归档包含主程序,macOS/iOS 归档包含完整.app结构。
Windows Release 冷启动烟雾测试:372.339 秒、37 个采样点的隔离实例测试,工作集从 307,494,912 B 到 304,701,440 B,私有字节减少 20,561,920 B,CPU 累计仅增加 2.125 秒,未出现随时间持续增长;记录为20260822T195317241Z-windows-release-smoke.json。
已知平台边界
- 斗鱼历史醒目留言:缺少已验证的稳定公开回补入口;进入直播间后的实时 SC、自动过期与切房隔离已覆盖;
- iOS:无签名设备归档供自行签名或 TrollStore 使用;播放器默认策略只作用于新配置,尊重已有用户选择(#770 修复即"iOS 新配置默认 IJK,已有显式选择不迁移");
- 外部直播接口会随平台变化,脚本结果记录的是发布时点状态,并保留失败平台的独立诊断——因此每次发布前都应重新运行 接口探测。
相关文档索引
- 近期 Issue 审计(2026-08-23):#765/#767/#769/#770/#771/#773/#774 的复现链路、根因与验证映射;
- 依赖与接口审计:后续版本的依赖升级、媒体栈同步与 16 KB 页面兼容性核验;
- 构建与发布:本机优先的构建门禁、签名流程与发布检查清单。
从 v2.6.0 的实践可以看出,pure_live 的稳定性方法论是"根因文档化 + 源码级修复 + 有界队列纪律 + 全平台门禁"四步闭环:每一次刷新、每一个异步回调都明确所有权与代次,每一个列表都明确滚动边界,每一张图片都按可见尺寸消费内存。这套做法不仅保证了本次五个平台产物的质量,也为后续版本(依赖审计、16 KB 对齐、媒体栈迁移等)奠定了可复现的发布基线。
- 音视频
- 直播
- 移动开发
【免费下载链接】pure_live
纯粹直播:哔哩哔哩/虎牙/斗鱼/快手/抖音/网易cc/YY直播/Twitch直播/SOOP直播/M38自定义源应有尽有。
相关推荐
pure_live v2.1.4 阶段更新全解读:平台接口稳定化、Windows 高刷新率适配与全平台构建交付
pure_live v2.1.4 阶段更新全解读:平台接口稳定化、Windows 高刷新率适配与全平台构建交付 导读 v2.1.4 是 pure_live(纯粹
音视频直播移动开发Pure Live v2.5.0 阶段稳定版技术解读:首页有界并发刷新、Android 刷新率三档策略与全平台稳定性治理
Pure Live v2.5.0 阶段稳定版技术解读:首页有界并发刷新、Android 刷新率三档策略与全平台稳定性治理 Pure Live(纯粹直播)是一个聚
音视频直播移动开发@tailwind-merge/next 版本策略全解读:dev 构建、0.x 阶段与稳定面承诺
@tailwind merge/next 版本策略全解读:dev 构建、0.x 阶段与稳定面承诺 本篇指南以仓库文档 packages/next/docs/ve
前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考