Repository navigation
Releases: jiayuxuan123/YunGet
Release list
云取 YunGet 2.7.10
云取 YunGet 2.7.10
这一版只做一件事:原生插件在手机上暂时停用。
为什么停用
原生插件(以前的"L2")是让插件直接以应用的身份跑在应用进程里的一档。它在手机上一直不稳:Android 10 之后系统不允许加载应用自己写出来的插件文件,最后只能靠"读进内存再加载"绕过,越往后系统限制越严。
而且实际效果也配不上这份麻烦——此前发布的四个网盘原生插件,做的事情只是把解析请求转回应用内部本来就有的实现,并没有带来应用自己做不到的能力。
停用之后会怎样
- 手机版现在只支持脚本插件(JS 插件),装上或更新后立即生效;
- 插件源里那四个网盘原生插件已经撤下,插件市场不再列出它们;
- 已经装过原生插件的用户:那几个插件留在手机上但不再参与工作,网盘解析走应用内置实现,功能不受影响;需要的话可以在插件列表里把它们卸载掉。
之后
等云推出了 Windows 版,原生插件会在 Windows 端启用——那里没有"不许加载可写目录里的插件文件"这条限制。
其它
- 插件页与帮助文档里关于能力级别的说明已同步改成现在的真实情况;
- README 里的版本号补上了(之前一直停在 2.7.5)。
云取 YunGet 2.7.9
云取 YunGet 2.7.9
修好两件影响日常使用的事:原生插件真正能加载了,图片终于能显示出来。
原生插件现在可以正常运行
上一版换了个目录存放插件包,真机上仍被系统拒绝——原因是 Android 10 之后系统禁止加载"位于可写目录里的插件文件",换到哪个可写目录都一样。这一版改为把插件包读进内存直接加载,全程不落盘,从机制上绕开了这条限制。
装过的网盘插件更新到本版并重启后即可正常加载;插件页不再显示"未运行"。
需要留意:加载原生插件需 Android 8.0 及以上;更低版本会给出明确提示,而不是静默失败。
图片能显示出来了
支持页的赞赏码、插件说明里的图片此前一律直连 GitHub,国内基本加载不出来。原因是应用里的"镜像"设置从来没有传给图片加载器——这个开关一直是空的。修复后:
- 图片与插件源、应用更新走同一套镜像规则(只对 GitHub 地址生效,失败自动回退直连);
- 设置页新增「GitHub 网络镜像」入口,填一个镜像前缀即可(此前只有插件源能单独配,图片没有任何入口)。
顺带说明:镜像只负责分发,插件照验 SHA-256 与签名,换镜像不会让没验签的插件装得上。
如果你之前遇到
- 插件页显示"原生插件未加载:重启后仍提示请卸载重装"——本版重启后应变为正常运行;
- 支持页只有文字、图片一直是空白——本版设置好镜像后即可显示。
云取 YunGet 2.7.8
云取 YunGet 2.7.8
修掉一个会让插件装不上的验签缺陷,建议所有用户更新。
修复
- 同一批签名的插件,一部分能装、一部分报"签名验证失败":应用内自带的一套 Ed25519 验签实现里,坐标运算的中间减法没有做取模归一化,导致减法出负数时结果算错——表现为与签名内容相关地随机失败(实测同一批 6 个插件,4 个能装、2 个报错)。这不是发布侧或网络的问题,插件包本身完全正确。修复后所有已发布的插件都能正常校验通过。
- 顺带修掉同一实现里另一处非规范余数(坐标取反)的隐患。
- 新增一条回归测试:直接用真实发布的插件文件与索引验证两条验签路径(平台实现与内置实现),防止这类"只有特定输入才触发"的缺陷再次溜过去。
如果你之前遇到
升级前若在插件市场看到"签名验证失败",那 2.7.8 之后重试即可正常安装;不需要重新下载任何插件源数据。
云取 YunGet 2.7.7
云取 YunGet 2.7.7
这个版本修复 2.7.6 在真机上的一个关键问题,建议所有安装了网盘插件的用户更新。
修复
- 原生插件重启后无法加载:Android 10 及以上系统拒绝从应用数据目录直接加载插件包(系统安全限制,日志表现为 "Writable dex file is not allowed")。现在加载前会把插件包复制到系统允许的位置,装过的原生插件重启后即可正常生效。2.7.6 里安装成功但一直显示"未运行"的插件,更新到本版并重启后即可使用。
- 安装失败时日志更具体:签名校验失败时,会把实际收到的文件指纹、索引里的签名字段与公钥来源写进诊断日志,远程排查"签名验证失败"类问题不再需要来回猜。
云取 YunGet 2.7.6
云取 YunGet 2.7.6
这个版本把插件系统真正接进了应用本体:此前完成的大部分插件能力只存在于底层,这一版补上了入口,并把一批影响日常使用的问题修掉。
原生插件可以装了
插件市场现在支持原生(L2)插件的完整流程:展示、安装、更新、卸载。原生插件以 jar 包分发,走与 JS 插件同一套校验(SHA-256 摘要、签名、吊销名单),体积上限 32 MB。
原生插件运行在应用进程内,没有沙箱隔离,安装确认页会如实说明这一点。它的任何变更(安装、更新、启停、卸载)都需要重启应用才能生效,相关操作都会明确提示。
插件能参与下载流程
- 下载完成的文件可以交给处理器插件做收尾(按扩展名匹配;处理器出错不影响下载结果,也不会动到已下载的文件)。
- 插件注册的下载后端会参与引擎的路由,优先级在内置后端之下。
网盘迁移说明
升级后首次启动会弹一次说明:网盘的分享解析将逐步交给插件处理,你的账号数据全部保留;插件不可用时自动回退到内置实现,功能不会中断。目前还没有网盘插件可装,这个弹窗只是提前把规则说清楚。
日志
- 应用崩溃会写进诊断日志。重启后可以在「设置 → 实时日志」切换到落盘来源查看历史,导出的日志文件也会带上崩溃报告。
- 实时日志页新增「落盘」来源,能看到之前运行的内容,不再只显示本次。
支持页
- 没有插件时显示内置的赞赏入口,不再是空白占位。
- 插件提供的支持内容支持远程图片(例如收款码)。官方源新增了一个零权限的「官方支持入口」插件,装上即可验证支持页的插件通道。
插件市场
- 源里的插件可以按分类筛选(链接解析 / 下载后端 / 引擎适配 等)。
- 已安装插件列表显示级别徽章(L1·JS / L2·原生)。
修复
- 插件市场、插件管理、支持页、实时日志等独立页面,按系统返回键会直接退出应用的问题。
- 插件详情里包含 Markdown 表格时应用闪退的问题(表格降级为等宽文本展示,内容不丢失)。
- 重新导入或回滚插件时,插件级别可能被重置的问题。
云取 YunGet 2.7.5
云取 YunGet 2.7.5
这个版本是网盘插件化的第一步,同时补齐了插件系统与日志系统的一轮能力。
网盘插件化
新增网盘插件统一契约:分享链接识别、分享解析、文件浏览、下载地址获取都由宿主定义接口,插件按契约实现。
下载地址仍由下载引擎执行——插件只提供结构化的下载信息(地址、请求头、有效期),不接管下载过程。多线程分片、断点续传这些能力不会因为用了插件而丢失。
各网盘能力差异较大(115 需要应用内登录页,蓝奏云没有分享解析接口),所以能力是声明式的:应用只调用插件声明过的能力,未声明的不会去调。
内置实现全部保留作为回退:插件未安装、未启用或不可用时,自动使用内置实现并记录日志,网盘功能不会中断。错误也按业务结果区分:需要提取码、需要登录、被风控、分享失效各有各的提示,而不是统一报「出错了」。
插件系统
- 更新失败可回滚:更新前保存当前版本,装载失败时自动回退并重新装载。应用启动时会对已启用的插件做一次健康检查。
- 插件源镜像:索引与脚本支持按源配置镜像地址,主地址不可用时自动尝试下一个,直连始终作为兜底。镜像只负责分发,脚本照常校验 SHA-256 与签名。
- 密钥轮换与吊销:内置公钥支持多个 keyId,新增吊销名单;被吊销的密钥即使签名有效也会拒绝安装。
- 引擎接入改为注册制:新增下载引擎不再需要修改应用代码,实现统一接口并注册即可,设置页会自动列出。不可用的引擎会说明原因,而不是列出来让用户选。
日志系统
重新做了分级、分类与实时查看:
- 按级别区分(详细 / 调试 / 信息 / 警告 / 错误),按子系统分文件(启动 / 网络 / 下载 / 解析 / 账号 / 插件 / 存储 / 界面 / 更新 / 网盘)
- 设置页新增实时日志,可当场查看并按级别过滤,错误行单独标出
- 所有错误额外汇总到一份文件,导出时排在最前面
- 补齐了下载与 Gopeed 引擎的日志落盘——此前这些路径只写系统日志,而新系统上系统日志缓冲只保留几秒,出问题时没有证据可回传
- 常见凭据格式在写入前会被自动遮蔽
支持页
赞赏入口改为由插件提供内容。没有插件时显示说明,而不是显示一份写死在应用里的清单。
已知限制
- 尚未迁移任何网盘到插件,当前全部仍走内置实现
- 原生插件在安卓上不支持热替换,安装、更新或启停后需要重启应用
YunGet 2.7.4
插件系统的四项补齐,外加路上发现并修掉的两个真问题。
引擎同步到 TurboDL 0.2.1.0。
更新失败可回滚(P4)
一次坏更新过去会让插件彻底不可用:脚本已经被覆盖,而没有退路。
现在更新前会把当前版本快照到 filesDir/plugins/rollback/<id>.json
(用侧车文件而不是加数据库列——这个项目已经出过一次迁移事故,不值得
为它冒风险)。装载失败会自动回滚、重新装载,并明确告诉你回退到了哪一版。
应用启动时也会体检已启用的插件:过去装载失败只有在你恰好打开插件页时才
暴露,不进那个页就永远发现不了,表现为"下载功能莫名失效,但插件列表看着
一切正常"。
源镜像与失败回退(P5)
候选顺序抽成了纯函数:本源自定义镜像 → 全局 GitHub 镜像 → 直连,
直连永远排最后。索引、脚本、清单、公钥四类请求走同一条回退链。
顺序本身就是要害——把直连排到前面会让插件流量默认过第三方 CDN,
而且界面上看不出来——所以测试把它钉死了。另外两条静态测试钉住
"镜像只负责分发、不产生信任":httpGetBytes() 里不允许出现
sha256Hex 或 PluginSignature。
密钥轮换与吊销(P6)
Ed25519 的签名永远有效,"吊销泄露的密钥"不可能靠验签失败实现,
只能显式拒绝那个 keyId,而且这个拒绝必须排在验签之前。否则换个源、
让它用自己的 publicKeyUrl 把作废的公钥递回来,验签照样通过。
内置公钥现在是"一个 keyId 一条 + 指纹"的表,轮换就是加一行;
吊销名单随应用发布而不是运行时拉取(一个能吊销密钥的网络端点,
也能放行伪造签名,或者把官方 keyId 拉黑做拒绝服务)。
selfCheck() 盯着轮换时最容易犯的那个错:把已吊销的钥匙忘在表里。
插件能力分级(P20)
索引条目新增 level 字段(js / native)。没写这个字段的老索引
按 L1 读,不受影响;写了当前应用不认识的值的条目不会被安装,而且
原因会显示在市场页上——否则"插件凭空从列表里消失"会被理解成
"源里没有",用户永远想不到要升级应用。L2 的界面措辞按事实写:
能力与宿主等同,没有沙箱;安装、启用、停用、卸载之后都会说明需要重启。
顺带修掉的两个问题
- 插件运行时的单例原本住在 UI 层,新增的启动体检会另建一个
PluginHost,导致每个已启用的插件都运行两遍。已移到 data 层。 refreshSource()没有任何调用方,它不走镜像、且与 MarketClient
重复写缓存。已删除。
YunGet 2.7.3:并发按实测吞吐收敛(不再盲目堆线程)
这次改了什么
内嵌引擎升到 TurboDL 0.2.0.9:下载并发现在会自己收敛,高线程数不再必然更快。
以前你把线程数调高,引擎就真的开那么多连接 —— 不管对面服务器受不受得了。现在它会看总吞吐:多开的连接如果没换来速度,就退回到更低的一档。
为什么值得装
实测同一台机器、同一个文件:
| 配置 | 吞吐 |
|---|---|
| 16 连接 | 30.6 MB/s |
| 64 连接 | 8.7 MB/s |
| 128 连接 | 6.1 MB/s(慢 5 倍) |
真实链路上也存在「加连接不再变快」的聚合限速。以前这两种情况引擎都不会自己退回来,你只能看着速度掉下去 —— 现在它会退,并在稳定一段时间后重新试一次。
有个细节值得说明
它不会因为「每条连接速度低」就降并发。有些服务端是按连接限速的(每条只有十几 KB/s),单连接速度本来就低,但总吞吐 = 单连接 × 连接数,这时候加连接依然有效,降档反而白白丢速度。所以判断只看总吞吐:
- 按连接限速 → 降档会让吞吐腰斩 → 引擎拒绝降档,保持高并发
- 整体封顶(不报错、默默限速)→ 加连接没有收益 → 引擎降到够用的最低档
两种情况都有专门的端到端测试,都在本机回环上跑过。
其它
- 下探的下限是 4 条连接,不是 1。连接数不只是带宽倍率,它还是应对链路不均的余量:实测有 1 条慢连接时,多块比少块快 3.78 倍。封顶时多留几条几乎不花成本,却能在某个 CDN 节点变慢时顶上。
- 收到 429/503 时的降并发仍然优先,不会被这套新逻辑抵消。
- 你在设置里配了「全局限速」时不做这项学习 —— 那是你自己设的上限,不是服务端的瓶颈。
- 默认行为对日常使用无变化:默认 16 线程下引擎本来就能跑满链路,新逻辑只是在「多开没用」时才介入。
测试
引擎侧 134 个用例全通过(0 失败、0 跳过),App 侧 147 个用例全通过。
还没验证的
- 真机未测:以上结论来自开发机与本机回环。你的网盘走的是哪家 CDN、属于哪种限速模型,只能在手机上才能确认。
- 多任务同时下载未专项测试:判断依据是单个任务的总吞吐,多个任务并行时会互相挤占带宽,理论上可能把别的任务的波动误读成当前档位的能力。已用中位数、10% 迟滞和持有期压制,但没有并发多任务的实测。
YunGet 2.7.2:修 OOM(下载时内存用满导致的崩溃)
YunGet 2.7.2:修 OOM(下载时内存用满导致的崩溃)
问题
崩溃日志:
java.lang.OutOfMemoryError: Failed to allocate a 64 byte allocation with 365360 free bytes
and 356KB until OOM, target footprint 268435456, growth limit 268435456
at okio.SocketAsyncTimeout.timedOut(JvmOkio.kt:155)
at okio.AsyncTimeout$Watchdog.run(AsyncTimeout.kt:197)
两处容易看错的地方:
一是崩在了 Okio Watchdog 线程上。 那只是"受害者"——它只是想在 socket 超时时关掉连接,
结果连 64 字节都分配不出来。真正的原因在别处。
二是 268435456 这个数字。 它是 256MiB,正好等于 Android 给这个应用的堆上限,
也正好等于 256 × 1MB。这不是巧合。
根因
引擎每个在飞连接分配一个读缓冲,缓冲大小是固定的 1MB。这个数看着不大,乘上连接数就大了——
而我在 2.6.22 那一版(提交 71d402e)把线程上限从 128 提到了引擎支持的 256:
256 连接 × 1MB = 268435456 字节 = 整堆
缓冲都是活对象(正被下载协程引用),GC 一个都回收不掉,所以是"占满"而不是"垃圾太多"。
触发条件很普通:线程数调到高 + 下载一个大文件。
128 那一版其实就已经吃掉半个堆,提到 256 只是把"危险"变成了"必然"。这是我引入的回归。
修法
给缓冲加总量预算(32MB),单连接缓冲按并发摊薄,而不是每连接固定 1MB:
| 场景 | 单连接缓冲 | 总占用 |
|---|---|---|
| 16 连接(默认设置) | 1MB | 16MB |
| 64 连接 | 512KB | 32MB |
| 256 连接 | 128KB | 32MB |
低并发行为完全不变(仍是 1MB),只有高并发才摊薄。128KB/连接远高于读 socket
的性价比区间,吞吐不会有可感差异——把 1MB 砍到 128KB 省下的是"空着的内存",
不是"正在用的带宽"。
摊薄的分母含并发任务数:预算是整个进程的,真实连接总数是"同时跑的任务数 × 每任务连接数"。
只按单任务摊,多任务并行时仍会突破(5 任务 × 256 连接 = 1280 条连接)。
兜底引擎(内置兼容 / aria2)是同一类缺陷(256KB/连接 × 256 = 64MB),用同一套口径一并管住。
为什么不是把线程数降回去
连接数是吞吐的关键手段:服务端按连接限速时,总吞吐 ≈ 单连接上限 × 连接数。
砍连接数是拿吞吐换内存,而真正的问题是内存花得不值——256 个连接每个独占 1MB 缓冲,
绝大部分时间都是空着的。
测试
- 引擎侧新增 7 例:把"总占用 ≤ 预算"这条不变量对任意配置组合钉住
(连接数 1..256 × 并发任务 1..64 全扫),并单独钉住事故场景与多任务场景; - App 侧新增 5 例:对设置页实际可选的全部线程/并发组合(10 × 5 = 50 种)验证;
- 全部 147 例通过。
说明
如果你之前把线程数设得比较高,升级后不必调回去——这一版就是让高线程数不再吃光内存。
自 2.7.0 起的三次崩溃(界面、插件误报、内存)至此都已修掉。
YunGet 2.7.1:修下载页崩溃与 JS 引擎误报
YunGet 2.7.1:修下载页崩溃与 JS 引擎误报
这两个问题都在 2.7.0 引入,也都由你真机实报。它们有一个共同点:错在判断,不在功能——
一个是把界面画崩的用法错误,一个是把好的引擎报成坏的。
下载页一进去就崩
崩在速度曲线的横轴上。我给横轴传了一个"返回空串"的格式化器,本意是让横轴不显示刻度,
但 Vico 明确禁止这么做:它把空标签当成用法错误,在绘制阶段直接抛异常,并在报错里写明了
正确做法——要控制哪些刻度显示标签,应该用 ItemPlacer,而不是让格式化器返回空串。
纵轴的"速度为零时不显示标签"是同一类错误,只是恰好没被触发到,一并改掉了。
横轴现在整条不传:它的刻度是采样点序号,对用户本来就没有信息量。
顺带把格式化逻辑抽出来配了测试,锁住"任何输入都不返回空串"这条契约。它能让应用崩溃,
但编译器不会拦——只能靠测试。覆盖了零、负数、各量级进位边界与极大值。
插件页把好的引擎报成了坏的
自检显示「加载原生库=通过、求值 + JSON 往返=失败、中断后实例仍可用=失败」,
于是已安装的插件一直停在"未运行"。
引擎是好的,是自检自己写错了。 失败的那两项都用 evaluate<String> 去接 JS 的数字,
而引擎的类型转换表里没有"数字转字符串"这条路径,必然报错。真正的问题在另外两项:
"超时可中断"和"内存上限生效"把"抛了任何异常"都算作通过——于是该报的报错了,
不该报的却显示通过,整张表都不能信。
现在:一律用 evaluate<Any?> 再自己判断数值(与引擎生产代码的做法一致);
"超时可中断"必须真的是被中断才算通过;"内存上限生效"必须真的是内存类错误才算通过。
失败时也会把真实的异常写进结论,而不是只报"失败"两个字——这次就是靠这一点才找到根因。
引擎侧也加了测试把这条类型契约钉住,避免以后又有人踩同一个坑。
说明
自检那张表以前给过你一个错误的结论,让你以为插件系统不能用。抱歉。
如果你还留着 2.7.0,升级到这一版后进插件页点一次「重新检测」,应该会全绿,
已装的插件也会开始运行。