Repository navigation
Releases: yaxiaiyuting/Arris
Release list
Arris v3.8.18-gpl
v3.8.18-gpl(versionCode 138)
音频流三段计时进日志
MediaHttp 挂上了 EventListener,每条音频请求都会打:
I MediaHttp: audio headers code=200 host=aqqmusic.tc.qq.com elapsed=449ms
I MediaHttp: audio call end elapsed=1321ms
I MediaHttp: audio call failed after 10012ms: SocketTimeoutException …
配合已有的 SongUrlFetcher: level=… ok in Xms 与 NcrustHttpTiming,
「点了歌之后那几秒花在哪」现在是三条可对齐的线:取链 → 建连/响应头 → 传输/解码。
实测参考(手机裸连):QQ 的 CDN TTFB 0.45s、877 KB 共 1.3s —— 服务端侧不慢。
补掉最后一个裸 OkHttpClient
AmllTtmlClient(TTML 歌词拉取)此前是 OkHttpClient().newBuilder() ——
唯一一个绕过 AppHttp 的客户端。本机 IPv6 不通时它会白等到超时,表现为「歌词一直不出来」。
现已并入 AppHttp。
HttpClientDnsAuditTest 也扩容了:OkHttpClient() 与 .newBuilder() 一并禁止 ——
它正是从这个缝里漏过去的。
校验
全量 JVM 单测 3505 项 0 失败。
Arris v3.8.17-gpl
v3.8.17-gpl(versionCode 137)
音源不可播时,不再是一行「看不出原因」的日志
排查 B站/QQ 失效时,logcat 里只有:
W PlayerViewModel: no playable url ... kind=UNKNOWN source=bilibili code=-
既没有网络请求记录,也没有原因 —— 分不清是「音源被关掉」「未登录」还是「接口失败」。
原因在 MusicSourceProvider.resolveUrlOutcome:provider 返回 null 时直接构造 UNKNOWN,不打日志。
现在补一条:
W MusicSourceProvider: resolveUrl returned null source=bilibili id=… level=…
(可能是该音源未启用、未登录,或该档位没有可播 URL)
这次 B站/QQ 失效的真实根因是设备上的
ncrust_settings.xml被清空
(bilibili_enabled丢失 ⇒ B站按 v3.1.0 的设计默认关闭),已恢复并验证。
这条日志是为了让下次不必再靠猜。
一并包含(3.8.16)
彩蛋「推荐一首」、AppHttp 统一网络入口(IPv4 优先 / 超时 / 共享连接池)、封面与艺人图修复。
校验
全量 JVM 单测 3505 项 0 失败。
Arris v3.8.16-gpl
v3.8.16-gpl(versionCode 136)
彩蛋里能推荐一首了
自由听众席(关于页版本号连点七次)原来只有标题和「继续听歌」,点开没什么可做的。
现在:
- 从你自己的收藏里随机挑一首(连续两次不重复),显示「为你推荐 / 歌名 / 艺人」
- 就听这首 直接进播放队列(与搜索、歌单同一个播放入口)
- 换一首 换下一首推荐
- 收藏为空时如实提示「收藏里还没有歌,先去挑一首吧」,不假装推荐
网络客户端统一接管:AppHttp
「本机 IPv6 到外网完全不通」这个坑,我们手动补了六次 dns ——
ncm 取链 → 音频流 → B站 → 检查更新 → 应用内下载 → 封面/艺人/专辑图。
每次都是有人新建了一个裸 OkHttpClient.Builder()。
现在把默认值收进 network/AppHttp:
AppHttp.builder() // 已带:IPv4 优先 DNS、连接 8s / 读 15s、retryOnConnectionFailure、共享连接池- 15 个文件 18 处 builder 全部改走它;主源码里裸
OkHttpClient.Builder()绝迹 - 补齐此前漏掉的客户端:Coil(封面/艺人头像/专辑图)、QqClient×2、QqQrClient、
MqttWebSocketTransport、NeteaseRecognizeProvider、163cn.tv 短链 HEAD - 防回归:
HttpClientDnsAuditTest扫描源码,裸建即红;
AddressPreference.ipv4First改成具名类IPv4FirstDns,测试可断言「默认客户端确实用了它」
校验
- 全量 JVM 单测 3505 项 0 失败
Arris v3.8.15-gpl
v3.8.15-gpl(versionCode 135)
应用内下载:4 段并发
APK 16 MB,在裸连(手机 → Cloudflare,RTT 450–560ms、丢包 50–66%)上单连接要好几分钟。
服务端其实一直支持 Range(实测 8 段并发各返回精确 1 MB),只是客户端没用。
现在:
- 先
Range: bytes=0-0探测,拿总长与ETag; - 4 段并发,每段独立
RandomAccessFile写自己的区间(互不重叠); - 收尾校验文件长度与
ETag(资产被替换就整体重来,不拼混合包)。
分流规则(什么时候不用多线程):服务端不支持 Range / 体积 ≤4 MB / 存在可续传残包 → 仍走原单流下载器;
任一段失败或用户取消 → 全体放弃并回落单流。快是加分项,下载成功是底线。
服务端改动
/api/releases 与 /api/latest 里的资产地址从 /dl/ 改为 /get/:
| 路径 | 行为 |
|---|---|
/dl/x.apk |
Cloudflare 静态直出,不认 Range(curl -r 返回整包) |
/get/x.apk |
走 Worker,自己实现 206 + Content-Range,支持续传与多线程 |
手机实测:HTTP/1.1 206,Content-Range: bytes 0-0/16069004,带 ETag。
一并修掉的
- 一起听成员侧漂移窗口 5 秒(3.8.14)
- 应用内下载补 IPv4 优先(3.8.13)
- 检查更新单对象归一化 + 11KB 响应(3.8.12)
Arris v3.8.14-gpl
v3.8.14-gpl(versionCode 134)
一起听:不再「一同步就暂停」
成员侧的漂移容差原来是 800ms —— 网络抖动 + 房主时间戳误差就能超过它,
于是成员每隔一两份快照就被纠正一次,听感就是「一直在被同步打断、老是暂停」。
现在的分层:
| 偏差 | 行为 |
|---|---|
| ≤ 5s | 完全不动(人耳在这个量级听不出差别) |
| 5s – 12s | 温和纠正:只把进度对到目标位置,不动播放/暂停态 |
| ≥ 12s | 强制对齐:位置与播放态一起同步 |
另外播放/暂停态现在要求连续两次快照一致才跟随(TRANSPORT_CONFIRMATIONS=2),
房主瞬时缓冲不会再顺手把成员一起停掉。
顺带修掉的两处网络问题(3.8.12 / 3.8.13 的内容)
- 检查更新:单对象响应归一化真正接上(此前一直判「解析失败」);
更新客户端补 IPv4 优先;响应从 509KB 压到 11KB,源按体积从小到大取。 - 应用内下载:下载客户端同样补 IPv4 优先 —— 修「第一次必失败、重试才成功」
(本机 IPv6 到 Cloudflare 完全不通,IPv4 200 / IPv6 15s 超时,实测记录在仓库里)。
校验
- 全量 JVM 单测 3503 项;唯一失败项是
QqLiveProbeTest的实时探针(本机出口 IP 被限,返回 403/500) - 新增
DriftWindowTest6 例钉住窗口边界(2s / 恰好 5s / 16s / 硬阈值 / 未起播 / 播放态)
Arris v3.8.13-gpl
v3.8.13-gpl(versionCode 133)
应用内下载:第一次必失败,第二次才连上
你报的现象 —— 第一次失败、重试成功 —— 是同一个根因的另一种表现:
真机实测(同一台手机、裸连):
| 请求 | 结果 |
|---|---|
curl -4 https://arris.de5.net/... |
HTTP 200,conn 0.39s |
curl -6 同一地址 |
15 秒超时 |
UpdateDownloader 自建的 OkHttp 没有 AddressPreference:第一次下载把 20s 预算全花在死掉的
IPv6 地址上 → 失败;重试时系统已把那个地址标记为不可用,才轮到 IPv4 → 成功。
修:下载客户端补 AddressPreference.ipv4First + retryOnConnectionFailure,连接超时 20s → 10s。
顺带加诊断日志(下次直接看得到卡在哪):
I UpdateDownloader: start host=arris.de5.net resumeFrom=0
I UpdateDownloader: response code=200 len=16069004 elapsed=3211ms
I UpdateDownloader: failed after 10012ms: SocketTimeoutException timeout
一并包含(3.8.9–3.8.12)
- 检查更新链路打通:单对象归一化、IPv4 优先、响应 509KB→11KB
- 浏览器入口与下载地址都指向官网(
site_url),GitHub 只作数据来源 - B站音源:
BiliApi客户端硬化(IPv4 / h1 / 读超时) - 音频流走 OkHttp(读 10s、连接池可清)、回前台刷新、取链阶梯记忆
校验
真机裸连实测检查更新:evaluate=Found(version=3.8.12, assetUrl=https://arris.de5.net/dl/...),
3.1 秒完成。
Arris v3.8.12-gpl
v3.8.12-gpl(versionCode 132)
这一版把「检查更新」整条链路真正打通,顺带修掉 B站音源。
应用内检查更新(真机裸连实测通过)
联调测试:临时发过一版 3.8.12,手机(3.8.11、无代理)冷启动后日志:
update-source ok host=arris.de5.net pages=1 bytes=360 elapsed=3104ms
update: head=[{"tag_name":"v3.8.12-gpl",...}]
update: evaluate=Found(version=3.8.12, assetName=Arris-v3.8.12-gpl.apk, ...)
测完已把那一版删除,编号不留洞(正式就是本版 3.8.12)。
过程中修掉的真问题:
- 单对象归一化没接线 ——
/api/latest返回的是对象,normalizePage()写了却没被调用,
于是每次都判「解析失败」(日志里的evaluate=null(解析失败)就是它)。 - 更新客户端没有 IPv4 优先 —— 本机实测:IPv4 到 CF 是 200(0.39s 建连),
IPv6 是 15 秒超时;系统把 IPv6 排前面,请求全耗在死地址上。 - 响应太大 ——
/api/releases509KB → 11KB;源按体积从小到大试:
/api/latest(1.5KB) →/api/releases→ jsDelivr 镜像 → 静态快照 → GitHub。 - 浏览器入口指向 GitHub —— 国内打不开。现在 Worker 给每条 release 附
site_url = https://arris.de5.net/#download,应用优先用它,老载荷才回落html_url。
B站音源失效
用你自己的 B 站 cookie 验过服务端整条链路都正常(search/view/playurl 全 code 0、
CDN 带 Referer 取流 206)。问题在 BiliApi 那条裸 OkHttp 客户端:
没有 IPv4 优先、自动协商 h2、30s 才超时。现已对齐 ncm:IPv4 优先、强制 HTTP/1.1、
连接 8s / 读 15s,并加 BiliApi.evictConnections()。
校验
- 全量单测 3497 项;5 项
QqLiveProbeTest是直连腾讯服务器的实时探针(本机出口 IP 被限,返回 500),与改动无关
Arris v3.8.9-gpl
v3.8.9-gpl(versionCode 129)
修:取链 0.7 秒,播放却卡十几秒才出声
真机日志:
NcrustHttpTiming: path=/eapi/.../player/url/v1 ttfb=264ms total=411ms
SongUrlFetcher: level=jymaster ok in 745ms
MediaSession: state=BUFFERING(6) position=138024 buffered=138024 speed=0.0
URL 拿到了、播放器进了 BUFFERING,但音频一个字节都没下来。
根因在音频流的数据源:media3 默认的 DefaultHttpDataSource 底层是 HttpURLConnection——
- 不认我们的 DNS 策略(代理 fake-ip、坏的 IPv6 地址照样往上连);
- 没有读超时 —— 半死连接只能等系统 TCP 超时(分钟级);
- 连接池不归我们管 ——
evictConnections()清不到它,网络切换后旧连接继续被复用。
网络一变(开关代理 / 后台唤醒),音频流就挂在那条半死连接上。
改为 OkHttpDataSource:
| 项 | 值 |
|---|---|
| DNS | AddressPreference.ipv4First |
| 连接超时 | 6s |
| 读超时 | 10s(半死连接 10s 内报错,交给 ExoPlayer 换新连接) |
| 连接保活 | pingInterval(15s) |
| 连接池 | 独立,网络变化 / 回前台时 MediaHttp.evict() 一并清 |
一并包含(3.8.5–3.8.8)
- 检查更新修好(资产名规则过时,此前永远「已是最新」)+ 裸连可用的三级取数
- 代理开关后未缓存的歌放不出来(清 JVM DNS 缓存)
- 回前台 ≥10s 自动刷新(清池 + 预热当前曲目)
- 首次取链提速(降级档位按账户记忆)
校验
- 全量 JVM 单测 3497 项 0 失败
- 16 KB page 合规(
p_align=0x4000、.so未压缩、zipalign -P 16通过)
Arris v3.8.6-gpl
回前台就刷新:离开 ≥10s 回来清连接池 + DNS 并预热当前曲目;修好应用内检查更新;修代理开关后未缓存的歌放不出来。
LTS 3.7.x · 长期维护线(滚动更新)
LTS 3.7.x · 长期维护线(滚动更新)
这条 Release 不是某个具体版本,而是 LTS 线的固定入口:每次 LTS 出包都会替换下面这些附件,
版本号看附件文件名。本页不会被标记为 Latest,也不会顶掉主线的「最新版」徽章。
当前 LTS 版本:v3.7.15-gpl(versionCode 102)
| 项 | 值 |
|---|---|
| 最低系统 | Android 7.0(minSdk 24) —— 想装 3.8.x 需要 Android 10+ |
| 附件 | Arris-v3.7.15-gpl.apk(≈ 12 MB) |
| 官网直链 | https://arris.de5.net/get/lts-latest.apk(支持断点续传/多线程,永远指向最新 LTS) |
| 旧版 | v3.7.14-gpl、v3.7.13-gpl 仍在 Releases 里,可回退 |
本版(3.7.15)回移的内容
- 开关代理后未缓存的歌放不出来:代理的 fake-ip(
198.18.0.0/15)与真实 IP 是两套解析结果,
而 JVM 的 DNS 缓存不随网络切换失效 —— 关掉代理后取链仍往假地址上连。
新增DnsCache(反射清缓存)并在网络切换与取链重试两处调用。 - 检查更新不再依赖 GitHub:首选
https://arris.de5.net/api/releases,
GitHub 原接口仅作兜底;api.github.com在国内经常裸连失败。 - 按发布通道过滤:3.7.x 只看到 3.7.x 与
lts-3.7,不会被推minSdk 29的 3.8.x。
LTS 线的取舍
LTS 只回移「设备兼容 / 正确性」修复,不带新功能:
- ✅ 会回移:竖屏封面遮挡、自动旋转默认值、地址族回退、主线程 socket 崩溃、DNS 缓存、视觉统一
- ❌ 不回移:听歌识曲、一起听(依赖 3.8 的协议与后端,且在 Android 7 设备上没有验证过)
校验
- 全量 JVM 单测 3399 项 0 失败
- 与 3.8.x 同一证书(
CN=Ncrust, OU=Dev, O=Ncrust),可直接覆盖安装