Skip to content

Releases: HelloEarth520/Music-Player

MusicPlayer v2.22.19 (code 60)

Choose a tag to compare

@HelloEarth520 HelloEarth520 released this 18 Sep 13:21

MusicPlayer v2.22.19

  • 版本号:versionName 2.22.19 / versionCode 60
  • 日期:2026-09-18
  • 本版主题:前台 CPU 占用优化——修复「炫彩流光文字」动画导致的每帧全视口重绘(实测 −26.5%)

需求背景

本版针对前台播放场景的 CPU / 内存占用做专项排查与优化。

真机(arm64-v8a)采样结论:

状态 CPU 占用
前台亮屏(暂停) 173.4%
前台亮屏(播放) 176.2%
后台(HOME) 14.8%
锁屏 5.7%
  • 前台高占用与是否播放无关(差 2.8 点),说明不是音频解码开销。
  • dumpsys gfxinfo:稳定 121fps、无掉帧;每帧 CPU 侧约 22ms(高刷屏每帧预算 8.3ms)、GPU 仅 3ms
    → 瓶颈在 CPU 侧渲染管线。
  • 内存无泄漏:连续切歌 16 次 PSS 471→551→515MB(回落),导入 1387 首仅 +12MB,线程 75→77。

归因过程(关键:第一次判断是错的)

第一轮判断(v2.22.18,已证伪)

按"毛玻璃很贵"的直觉,把整屏模糊从 #bg-veil 的 backdrop-filter 迁到壁纸层 filter,
并下调各处模糊半径、加 contain: layout paint。实测只降约 7%,与预期严重不符。

第二轮:CDP 逐项 A/B 归因(决定性)

经 adb forward 接 WebView 的 webview_devtools_remote_<pid>,用 Runtime.evaluate
注入覆盖 CSS,在同一进程、同一首歌、同一次会话里逐项开关。
共 5 轮、47 个对照场景(工具:_perf/cdp_attr.mjs,Node 22 原生 WebSocket,零第三方依赖)。

场景 CPU 均值 Δ 判读
基线 163.7% 0 —
停 19 个列表项标题的 fx-flow 136.4% −31.5 唯一显著项
↳ 复测(重复性) 136.6% −31.3 复现性 ±0.2
停跑马灯环 ring-run 170.5% +7.5 无成本
停歌词流逝 lyric-flow 169.5% +1.6 无成本
停顶栏 / 栏头 / 数值类 164.6~166.6% −1.3~−3.3 噪声
关全部 transition 163.3% +3.1 无成本
关全部 backdrop-filter 153.2% −10.5 约 −6%,非主因
关 filter / 壁纸模糊 165.5% +1.8 无成本
关阴影 161.7% −2.0 噪声

根因:播放列表 19 个曲目标题用 background-clip:text + 动画 background-position
实现「炫彩流动文字」(@keyframes fx-flow)。该动画不可合成到 GPU 合成线程,
每一帧都要在主线程重新栅格化文字渐变色。

非线性发现:顶栏 / 栏头 / 数值三项各自单停只值 1.3~3.3 点,但连同列表项标题一起停掉却 −54.2 点
("关全部动画"上限 −83.5)。说明成本主体是「这一帧到底有没有跨视口重绘」——
只要视口内任意一处持续变化,整条管线(主线程样式/布局/绘制 + 合成 + 所有 backdrop-filter
区域重新快照并模糊)就每帧全量运转;此时增删单个元素的边际成本很小。

实现

  • www/style.css:6 处 fx-flow 动画的时间函数 linear → steps(240, end):

    /* 改前 */  animation: fx-flow var(--fx-speed, 10s) linear infinite;
    /* 改后 */  animation: fx-flow var(--fx-speed, 10s) steps(240, end) infinite;

    渐变更新率由约 121 次/秒降到约 8 次/秒(--fx-speed 默认 10s 时约 24 次/秒)。
    动画外观不变:位移速度仍约 70px/s,每步仅约 1% 背景宽(235px 标题约 7px),
    柔和渐变下肉眼无差别。语法注意:steps() 与 linear 互斥,不能同写(否则整条声明失效)。

  • www/player.js:v2.22.18 的性能改造保留(壁纸模糊迁移、coverTick 30fps 闸门 + 页面隐藏门控、
    通知推送四要素去重、EQ 渐变对象按高度量化缓存),并修正其中已证伪的注释。

  • web-demo/style.css、web-demo/demo.js:同步同一套改造。

  • 清理:www/_perf_bak_20260918/(会被 cap copy 打进 APK)已移出到项目根。

  • android/app/build.gradle:versionCode 59 → 60,versionName 2.22.18 → 2.22.19。

  • www/index.html:设置页版本号 v2.22.19。

验证

修复前预演(CDP A/B)

场景 CPU Δ
基线 164.5% 0
全部 fx-flow 元素加 steps(240, end) 121.3% −43.2(−26%)
仅列表项标题完全停掉(对照) 137.6% −26.9

量化后的成本已与"完全删掉动画"持平 —— 拿到全部收益、零视觉损失。

修复后实测(真机,同方法)

场景 v2.22.18 v2.22.19 变化
前台亮屏播放(基线) 164.5% 120.9% −26.5%
↳ 基线复测 167.9% 133.6% −20.4%
关全部动画(下限对照) 79.5% 98.0% / 86.5% 差距收窄至约 23 点

与预演预测的 −26% 吻合。网页侧核对计算值确认修复生效:

TIMING_FN: {"titleName":"fx-flow","titleTF":"steps(240)","titleDur":"30s",
            "clockName":"fx-flow","clockTF":"steps(240)"}

构建与归档

  • cap copy → assembleDebug BUILD SUCCESSFUL(18s + 13s 两次,JDK 21)。
  • 装机:adb install -r -d → Success;dumpsys package 确认 versionCode=60 / versionName=2.22.19。
  • 归档:python _release/archive_release.py 2.22.19 60
    (source 快照 32.6MB + APK 本版/回滚基线 + source zip + website zip)。
  • webdemo 包:python _release/pack_webdemo.py 2.22.19 60(本版新增工具)。
  • 回滚基线:APK/app-v2.22.18-code59.apk(亦在 <归档>/_apk_backup/)。

装机记录

  • 覆盖安装成功,用户数据保留。
  • 冷启动正常,无 FATAL / AndroidRuntime 异常;播放正常。
  • 修复后新会话实测前台 CPU 稳定在 100~119%。

测量局限(如实说明)

  1. 帧率不可锁:部分厂商 ROM 忽略 settings put system peak_refresh_rate,设备稳定在约 121fps。
    若能在 60Hz 下运行,CPU 占用预计近似减半——这是一条尚未利用的优化路径,但系统层无可用接口。
  2. 存在状态依赖:同一组覆盖条件在两次独立测量中最大差 38 点(67.9% vs 106.0%),
    疑与温度、调度、播放进度有关。故只采纳多轮重复一致的项;单次极端值不予采信。
  3. CPU 口径:为「相对单核」百分比,8 核满载为 800%;含进程内所有线程。
  4. GPU 归因有限:dumpsys gfxinfo 的 GPU 耗时(约 3ms)未完整覆盖 WebView 自身渲染管线。

构建备忘

同 v2.22.13:cap copy android → JAVA_HOME=<JDK 21> +
ANDROID_HOME=<ANDROID_SDK> + NODE_OPTIONS="--use-system-ca" → gradlew assembleDebug。

归因工具复现方式:

ADB=<adb路径>
PID=$("$ADB" shell pidof com.musicplayer.app | tr -d '\r')
"$ADB" forward tcp:9222 localabstract:webview_devtools_remote_$PID
NODE_OPTIONS="--use-system-ca" node _perf/cdp_attr.mjs com.musicplayer.app "$PID" run

MusicPlayer v2.22.18 (code 59)

Choose a tag to compare

@HelloEarth520 HelloEarth520 released this 18 Sep 13:19

MusicPlayer v2.22.18

  • 版本号:versionName 2.22.18 / versionCode 59
  • 日期:2026-09-10
  • 本版主题:移除 INTERNET 权限——系统级保证纯本地播放、零联网

需求背景

已厘清 INTERNET 权限的来源:Capacitor 默认设计是联网加载页面(支持 server.url
远程加载 / live-reload 热更新),模板无条件带入 android.permission.INTERNET——
与本应用 JS 是否发请求无关(权限是静态清单声明,不看运行时行为)。

核实结论(本版动前的勘察):

  1. 权限声明在 android/app/src/main/AndroidManifest.xml(cap add android 一次性拷入,
    cap sync 不重写);
  2. capacitor.config.ts 未配 server.url,页面经 WebView 进程内 scheme 拦截
    (https://localhost / _capacitor_file_)加载,不出网卡;
  3. grep 全量 www/ JS:仅有的 fetch 全是本地 URL(音轨 blob/content://、FFmpeg wasm
    本地文件),零远程请求;
  4. 本地播放、SAF 选歌、转码、媒体通知均不依赖该权限。

→ 结论:这条权限是「纯预留」,删除后系统将强制禁止本应用建立任何网络连接,
作为隐私卖点保留;未来加在线功能(歌词抓取/在线流/检查更新)或需 live-reload 调试时,
在 Manifest 注释处加回一行即可。

实现

  • AndroidManifest.xml:删除 <uses-permission android:name="android.permission.INTERNET" />,
    原位置留还原指引注释;
  • android/app/build.gradle:versionCode 58 → 59,versionName 2.22.17 → 2.22.18;
  • www/index.html:设置页版本号 v2.22.18。

验证(aapt 实测最终 APK)

aapt dump permissions app-debug.apk 输出(无 INTERNET,且无依赖库合并回注):

uses-permission: android.permission.READ_MEDIA_IMAGES
uses-permission: android.permission.POST_NOTIFICATIONS
uses-permission: android.permission.FOREGROUND_SERVICE
uses-permission: android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK
permission: com.musicplayer.app.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION(androidx 自动生成)

构建与归档

  • cap copy → assembleDebug BUILD SUCCESSFUL(20s,JDK 21)。
  • 归档:python _release/archive_release.py 2.22.18 59(source 快照 32.3MB + APK 本版/回滚基线 + 双 zip)。
  • 回滚基线:APK/app-v2.22.17-code58.apk(亦在 <归档>/_apk_backup/)。

装机记录(待补)

  • adb push 成功;pm install -r 两次 INSTALL_FAILED_ABORTED: User rejected permissions
    (设备熄屏时弹窗弹不出直接秒拒;唤醒亮屏后 pm install 挂起等待设备端确认)。
  • 待在平板上确认厂商USB 安装弹窗后补装,并实测:
    1. 正常启动、扫描/播放本地歌曲(验证 scheme 拦截加载不受影响);
    2. 转码开关开启后 FFmpeg wasm 本地加载正常;
    3. 设置 → 关于,版本显示 v2.22.18;
    4. 系统设置 → 应用 → 权限,确认无「网络」相关项。

构建备忘

同 v2.22.13(cap copy → JAVA_HOME=<JDK 21> → assembleDebug
→ adb push /data/local/tmp + pm install -r)。
装机坑补充:厂商设备熄屏状态下 USB 安装确认弹窗不弹、直接秒拒
(INSTALL_FAILED_ABORTED: User rejected permissions);先唤醒亮屏再装。

MusicPlayer v2.22.17 (code 58)

Choose a tag to compare

@HelloEarth520 HelloEarth520 released this 15 Sep 16:27

MusicPlayer v2.22.17

  • 版本号:versionName 2.22.17 / versionCode 58
  • 日期:2026-09-16
  • 本版主题:定位滚动改为「全过程可见 + 时长可自定义」(v2.22.16 实测反馈)

需求背景(v2.22.16 实测反馈)

v2.22.16 的定位按钮能定位,但距离大于 10 行就是瞬间跳转——看不到滚动过程,
既没有向上/向下的方向感,也看不到列表进度条移动,还是不知道跳到哪去了。

要求:

  1. 不管多远都慢慢滑过去,能看清是向上还是向下、进度条跟着走;
  2. 滚动时长可自定义:设置里加滑块 + 数字,1 ~ 15 秒。

实现

① 弃用 CSS scrollTo({behavior:'smooth'})

CSS smooth 的时长不可控(浏览器自定),且长距离会滚到数秒以上、短距离又太快。
改为自定义 rAF 动画逐帧写 scrollTop:

const ease = t => (t < 0.5 ? 2*t*t : 1 - Math.pow(-2*t + 2, 2) / 2);  // easeInOutQuad
playlistEl.scrollTop = start + delta * ease(p);                        // p = 已过时间/总时长
  • 逐帧改 scrollTop 会照常触发 scroll 事件 → 驱动虚拟滚动渲染 → 列表与进度条实时可见地移动;
  • 落位闪烁仍挂在滚动停止回调(120ms)里,时序逻辑不变,另加 duration + 600ms 兜底定时器。

② 时长规则

  • 距离 ≤10 行:固定 0.6 秒(3 秒爬 10 行过于拖沓);
  • 距离 >10 行:采用设置值,范围 1 ~ 15 秒,默认 3 秒。

③ 设置项「定位滚动 (Locate)」

  • 设置面板新增滑块 set-locate-sec-slider(1~15,步进 1)+ 右侧数值 set-locate-sec-val;
  • 存储:appSettings.locateScrollSec,settingsVersion 5 → 6 并附迁移(非法值回退 3);
  • web-demo 同步持久化到 mp_appearance.locateScrollSec。

④ 可打断

pointerdown / wheel / touchstart 任一发生即 cancelAnimationFrame 取消动画,
用户中途触摸或滚轮可立刻接管滚动,不会跟用户抢。

同步范围

  • www/:index.html(设置组 + 版本号 v2.22.17)、player.js(rAF 动画 / 时长规则 / 设置绑定 / 打断 / v6 迁移)
  • web-demo/:index.html(设置组 + 版本号)、demo.js(rAF 动画 + 滑块 + 持久化)
  • _site_work/site/web-demo/:已 cp + cmp -s 逐字节校验一致

构建与装机

  • cap copy → assembleDebug BUILD SUCCESSFUL(15s,JDK 21)。
  • 装机:Android 平板 pm install -r Success(保留数据,已核验 versionCode=58 / versionName=2.22.17)。
  • 回滚基线:APK/app-v2.22.16-code57.apk(亦在 <归档>/_apk_backup/)。
  • 新增归档工具:_release/archive_release.py(python archive_release.py 2.22.17 58 一条命令出 source/APK/两个 zip)。

待实测

  1. 距离远(如 1387 首跳到第 1000 首)时,是否能看到持续几秒的滚动过程与进度条移动。
  2. 设置里把时长拉到 1 秒 / 15 秒,滚动快慢是否随之改变。
  3. 距离 ≤10 行时是否快速到位(0.6 秒),不拖沓。
  4. 滚动途中用手指一碰,是否立即停下并交由手动控制。
  5. 落位闪烁是否仍然明显(v2.22.16 行为不变)。

构建备忘

同 v2.22.13(cap copy → JAVA_HOME=<JDK 21> → assembleDebug
→ adb push /data/local/tmp + pm install -r)。

MusicPlayer v2.22.16 (code 57)

Choose a tag to compare

@HelloEarth520 HelloEarth520 released this 15 Sep 16:26

MusicPlayer v2.22.16

  • 版本号:versionName 2.22.16 / versionCode 57
  • 日期:2026-09-15
  • 本版主题:目录头新增「定位当前播放」按钮(长列表 / 随机播放场景定位)

需求背景(v2.22.15 实测反馈)

导入 1387 首音乐后:

  1. 随机播放时播放列表不跟随定位到当前曲目;
  2. 手动滑动很久才能找到,且快速滑动时看不清当前项的颜色变化(当前项仅有 2px 跑马灯细环),导致完全不知道播到哪一首。

实现

① 目录头新增定位按钮

  • 位置:音乐目录 头栏右侧,「展开 ▾」与「+ 添加目录」之间(指定“加号左边”)。
  • 样式:26×26 圆形、青色 #22d3ee,与 .btn-add-dir 同规格;内含靶心 SVG。

② 居中滚动(locateCurrentTrack)

target = idx * itemH - (viewH - itemH) / 2      // 居中
target = clamp(target, 0, playlistLen * itemH - viewH)
  • 距离 >40 行(≈2720px)用 behavior:'auto' 直接跳(千首列表 smooth 会滚好几秒),
    ≤40 行用 smooth;老 WebView 不支持 options 形式时回退 scrollTop = target。

③ 落位后整卡闪烁 1.5s

  • .locate-flash 关键帧:青色底 + 外扩光环脉冲两次后淡出,卡片边框同步高亮。
  • 时序要点:虚拟化列表滚动中会 container.innerHTML='' 重建节点,闪烁类会丢,
    因此挂在 onPlaylistScroll 的**滚动停止回调(120ms)**里,而不是滚动发起时。
  • 兜底:目标位置与当前位置相同(不产生 scroll 事件)时,由 900ms(auto 时 400ms)
    定时器强制触发一次闪烁。

④ 设计取舍:不做「切歌自动跟随」

历史结论(见 renderVisibleItems 注释):自动 scrollIntoView 会在用户手动浏览列表时
把列表强行拉回当前曲目。因此改为用户主动点击触发,不自动定位。

同步范围

  • www/:index.html(按钮 + 版本号)、style.css(按钮样式 + @keyframes locate-flash)、
    player.js(locateCurrentTrack / flashCurrentPlaylistItem + 按钮绑定 + 停稳回调钩子)
  • web-demo/:同样三件套(全量渲染无虚拟滚动,直接 scrollIntoView({block:'center'}) + flash)
  • _site_work/site/web-demo/:已 cp + cmp -s 逐字节校验一致

构建与装机

  • cap copy → assembleDebug BUILD SUCCESSFUL(17s,JDK 21)。
  • 装机:Android 平板 pm install -r 覆盖安装 Success(保留数据)。
  • 回滚基线:APK/app-v2.22.15-code56.apk(亦在 <归档>/_apk_backup/)。
  • Android 手机 未连接,本版按钮在手机横屏下的表现待验证。

待实测

  1. 随机播放中途点定位按钮:当前曲目是否居中且闪烁明显可见。
  2. 距离极远(>40 行,如 1387 首跳到第 1000+ 首)时是否瞬间跳转而非长时间滚动。
  3. 连点两次是否每次都重新闪烁(强制重排重挂动画)。
  4. 未播放任何歌曲时点击,是否提示「当前没有正在播放的歌曲」。
  5. 回归:目录折叠、+ 添加目录、炫彩分区等 v2.22.13~15 内容不受影响。

构建备忘

同 v2.22.13(cap copy → JAVA_HOME=<JDK 21> → assembleDebug
→ adb push /data/local/tmp + pm install -r)。

MusicPlayer v2.22.15

Choose a tag to compare

@HelloEarth520 HelloEarth520 released this 10 Sep 16:07

MusicPlayer v2.22.15(versionCode 56)

Android 安装包 + 完整源码 + Web 演示站。跨平台本地音乐播放器(Capacitor 8 + Electron 30),彩色渐变极简风格,一切皆可自定。

本版亮点:自定义程度大幅提升 + 手机横屏适配(v2.22.13 ~ v2.22.15 三版累积)

  1. 主界面元素显隐开关:歌曲名称 / 创作者 / 进度条 / 音量条 / 歌词五项独立开关(设置 → 主界面元素显隐)。
  2. 布局调节:设置内进入编辑态,顶栏上下拖调高度、侧栏左右拖调宽度,实时跟随、松手保存、一键恢复。
  3. 音乐目录折叠:目录超过 2 个自动折叠并显示「展开 N 个 / 收起」,≤2 个保持原样。
  4. 封面左右滑切歌:左滑下一首、右滑上一首,跟手 30% 位移、回弹动画,纵向滑动不误触。
  5. 炫彩文字分区开关 + 自定义颜色:顶栏区 / 主界面区 / 设置与弹窗区三分区独立开关;关掉某区炫彩后可填颜色编号(如 #ffaa00)改用自定义纯色。Web 演示版已同步全部以上功能。
  6. 手机横屏修复:右半区 UI 可完整滚动到底(歌词封顶 26vh + 控件压缩);播放列表滑动性能优化(rAF 节流 + 区间缓存,滑动期禁用重阴影过渡)。
  7. 横屏裁剪修复(v2.22.15):自定义图片背景的裁剪候选框首开 4×4 无法拖动的时序 bug(隐藏态量尺寸为 0);裁剪盒在矮视口不再溢出屏幕、确认按钮可点;裁剪比例改用视口实测宽高比。

Assets 说明

  • app-v2.22.15-code56.apk — Android APK(arm64-v8a,Android 16 平板实测)
  • MusicPlayer-source-v2.22.15.zip — 完整源码快照(android/www/web-demo + 根三件套,不含 node_modules)
  • MusicPlayer-website-v2.22.15.zip — Web 演示站(首页 + 全功能演示播放器)

MusicPlayer v2.22.12

Choose a tag to compare

@HelloEarth520 HelloEarth520 released this 09 Sep 23:31

MusicPlayer v2.22.12

  • 版本号:versionName 2.22.12 / versionCode 53
  • 日期:2026-09-09
  • 反馈问题(v2.22.11 实测):
    1. 锁屏/通知栏暂停 → 通知整个消失,需回应用手动播放才重新出现。
    2. 小窗展开后关闭 → 不在后台;播放中关闭后残留,只能长按应用→应用信息→强停。
    3. 原子岛仍未上(需用户确认已开「显示原子通知」开关)。

根因与修复

① 暂停通知消失(Java 原生,根因)

  • 根因:update() 原逻辑"暂停 → MediaControlService.stopIfRunning()"摘除前台。
    部分厂商 ROM 会清理"暂停且无前台服务"的媒体通知。此前 v2.22.10 靠 JS 每 500ms
    轮询重推把通知"续命"顶回来 → 表现为"收起→重弹"闪烁;v2.22.11 暂停跳过轮询后
    没人顶 → 通知被系统清掉即彻底消失。两版同一病根,v2.22.11 只是暴露了它。
  • 修复:前台媒体服务生命周期跟随"媒体会话"而非"播放/暂停"——update() 无论
    播放暂停都 MediaControlService.start()(幂等),服务从首次播放一直保持到真正停止
    (hide() / 退出)。暂停态通知成为系统级常驻前台媒体通知(网易云等标准行为):
    不被清理、无 ongoing 翻转、不闪烁。pushNow()(通知授权回调)同步为"有内容即保
    持前台"。JS 端 v2.22.11 的"暂停跳过 500ms 轮询"保留(现在无需续命,纯省电)。

② 小窗关闭残留(Java 原生)

  • 根因:音频在 WebView,Activity 被销毁(小窗 × / 划掉任务 / 返回退出)即播放
    终止,但 MediaControlService(前台服务)无人通知停止 → 通知/服务残留,
    只能去应用信息强停。
  • 修复:MainActivity.onDestroy() 中 isFinishing() 为 true 时调用新增的
    MediaSessionPlugin.cleanupForExit():停服务 + 撤通知 + session 停用,干净退出。
    Home 键回桌面仅 onStop 不销毁,后台播放不受影响。

装机

  • Android 平板,pm install -r 覆盖安装 Success(保留数据)。
  • dumpsys 确认 versionCode=53 / versionName=2.22.12。

待实测

  1. 播放 → 暂停(通知栏按钮 & 锁屏):通知是否不再消失、不再"收起→重弹"。
  2. 小窗:播放中划小窗 → 展开 → 关闭:是否干净退出(通知撤、播放停),无需强停。
  3. 原子岛:**设置→通知与状态栏→原子岛→开「显示原子通知」**后播放,是否上岛
    (v2.22.12 起播放/暂停都保持前台媒体服务,上岛条件比以往更充分)。

MusicPlayer v2.14.0 (Android APK)

Choose a tag to compare

@HelloEarth520 HelloEarth520 released this 31 Jul 08:14

MusicPlayer 三端音乐播放器 v2.14.0 发布。

本 Release 提供安卓安装包 MusicPlayer-v2.14.0.apk(约 43.7 MB)。

安装方式:

  1. 下载 APK 到安卓设备;
  2. 在系统设置中允许「未知来源」安装;
  3. 点击 APK 完成安装。

默认界面标题:MusicPlayer-一切皆可自定。源码以 MIT 许可证开源,位于仓库主分支。