原生插件
Android 版的原生能力由 5 个自研 Capacitor 插件 + nodejs-mobile 内嵌 Node 运行时 + 2 个 Rust workspace 包 组成。JS 侧统一通过 @capacitor/core 的 registerPlugin 声明,位于 src/plugins/。
插件总览
| 插件名 | 职责 | 原生实现 |
|---|---|---|
AndroidNativePlayback | 播放引擎、MediaSession、桌面歌词、频谱 | playback/PlaybackManager.java 等 |
AndroidDownload | SAF 目录授权、下载、本地音乐扫描 | download/AndroidDownloadPlugin.java |
AndroidCache | 音频 / 歌词 / 封面 / 列表缓存与 LRU 清理 | cache/CacheStorage.java 等 |
AndroidLocalLyric | 本地歌词目录扫描与匹配 | lyric/AndroidLocalLyricPlugin.java 等 |
AndroidShare | 歌词海报保存到相册 / 系统分享 | AndroidSharePlugin.java |
插件在 MainActivity.java 中注册;AndroidManifest.xml 声明所需权限与两个前台服务:
.playback.PlaybackService(MediaSessionService,mediaPlayback 类型).playback.FloatingLyricService(桌面歌词悬浮窗)
所需权限:INTERNET、WAKE_LOCK、FOREGROUND_SERVICE、FOREGROUND_SERVICE_MEDIA_PLAYBACK、POST_NOTIFICATIONS、SYSTEM_ALERT_WINDOW。
AndroidNativePlayback
核心播放插件,封装 ExoPlayer 与 MediaSession。
主要方法
load / play / pause / stop / seek / setVolume / setRate / setEqualizer / updateMetadata / updateQueueContext / getState / syncRemoteState / syncApiContext / setAllowMixWithOthers / setShowStatusBar / setHideNavigationBar / setImmersiveLandscape / showFloatingLyric / hideFloatingLyric / updateFloatingLyricData / updateFloatingLyricProgress / updateFloatingLyricSongInfo / updateFloatingLyricConfig / checkOverlayPermission / requestOverlayPermission / requestNotificationPermission / enableVisualizer / prefetchAudio / shutdownApp
事件
| 事件 | 载荷 |
|---|---|
playbackStateChanged | 播放 / 暂停 / 停止状态 |
progressChanged | 当前进度 |
ended | 单曲播放结束 |
error | 播放错误 |
customAction | 原生侧发起的动作:next / previous / play / pause / favorite / desktopLyric / collapse / autoNext / trackChanged / requestUrls |
visualizerData | 256 bin FFT(base64)+ 低频能量,约 30Hz |
diagnosticLog | 原生日志 |
双播放模式
- 原生 / 本地模式:JS 通过
updateQueueContext推送「当前曲 ± N 首」的队列窗口(含元数据与已解析 URL 或 null),原生侧在 WebView 被冻结时也能自主处理播完切歌、上一首 / 下一首,并通过PlaybackUrlResolver自行调用内置 API 解析播放地址(64 条 LRU + 30s 负缓存); - 远程模式:WebView 的
HTMLAudioElement实际发声,原生侧仅镜像状态到通知栏与 MediaSession,播放期间持有PARTIAL_WAKE_LOCK(上限 4 小时)。
「允许与其他应用同时播放」开启时走原生 ExoPlayer 且 handleAudioFocus=false;关闭时走 WebView 音频并由系统独占音频焦点。
音频处理链
Media3 PCM 处理链中挂载:
EqualizerAudioProcessor:10 段双二阶均衡器(31 / 63 / 125 / 250 / 500 / 1k / 2k / 4k / 8k / 16k Hz,±12dB),由均衡器弹窗的预设(原声 / 流行 / 舞曲 / 摇滚 / 古典 / 爵士 / 人声 / 重低音 / 自定义)驱动;FftAudioProcessor:256 bin FFT 与平滑低频能量,供播放页频谱与流体背景跳动使用;enableVisualizer(false)时 CPU 开销归零。
AndroidDownload
基于 SAF(ACTION_OPEN_DOCUMENT_TREE + DocumentFile):
pickDownloadDirectory/getDownloadDirectoryInfo:下载目录授权与查询;downloadFile({ url, fileName, directoryUri, subPath }):流式下载,subPath实现「按歌手 / 歌手\专辑」智能分类,已存在则跳过,进度通过downloadProgress事件上报;writeTextFile:写出歌词 / ASS / 元信息等侧车文件;pickLocalMusicDirectory/scanLocalMusic:本地音乐目录授权与扫描,用MediaMetadataRetriever读取标题 / 歌手 / 专辑 / 时长 / 码率。
AndroidCache
统一缓存存储,类型分为 lyrics / covers / list-covers / list-data / exo:
exo为 ExoPlayerSimpleCache目录,参与全局 LRU;- 方法:
read/write(base64)/remove/list/clear/clearAll/getStats/setMaxBytes/enforceLimit; - 音频预取 TTL 为 50 分钟,每 30 分钟清扫一次;设备剩余空间不足时上限自动下调至剩余空间的 60%。
AndroidLocalLyric
pickLyricDirectory/scanLyricDirectories/readLyricFile/findSidecarLyric;- 支持
ttml/yrc/lrc三种格式; - 匹配规则:
歌曲ID.ext、歌名.歌曲ID.ext、TTML 内ncmMusicId元数据、音频同目录同名侧车文件; - 路径映射与元数据解析有独立单元测试。
AndroidShare
saveImage:经 MediaStore 保存到Pictures/SPlayer(歌词海报);shareImage:经 FileProvider 调起系统分享面板。
nodejs-mobile-cordova
内嵌 Node.js 运行时,负责在设备上运行内置网易云 API。
- JS 侧入口类型声明在
src/env.d.ts(window.nodejs); - 原生桥位于
android/app/libs/cdvnodejsmobile/(native-lib.cpp/cordova-bridge.cpp+ 各 ABI 的libnode.so); postinstall脚本会修补插件build.gradle以适配 Capacitor 的assets/public目录结构。
Rust workspace 包
根目录 Cargo.toml 定义 workspace(release profile 开启 LTO 与 opt-level 3),包含两个包:
ferrous-opencc-wasm
- 基于
wasm-pack build --target web构建,包装ferrous-opencc; - 暴露
TextConverter类:new(config)(s2t/s2tw/s2hk/s2twp)+convert(input); - 在 Android WebView 内以 WASM 运行,供「更喜欢繁体中文」歌词转换使用;
- 通过别名
@opencc引入,预构建产物pkg/已提交进仓库。
external-media-integration(仅桌面端)
- 基于
napi-rs的 Node 原生扩展,实现 Windows SMTC / Linux MPRIS / macOS NowPlaying 与 Discord RPC; - Android 版不使用该模块:对应能力由原生 MediaSession 与通知栏承担,设置中的 SMTC / Discord 选项在 Android 版中不显示;
- 构建产物被 Vite 外部化,不参与 Android 打包。
Android 工程要点
| 项 | 值 |
|---|---|
| 包名 | top.imsyy.splayer.android |
| minSdk | 29(Android 10) |
| compile / targetSdk | 36 |
| ABI 分包 | armeabi-v7a / arm64-v8a / x86 / x86_64(不产 universal 包) |
| 媒体库 | androidx.media3:media3-exoplayer:1.8.0、media3-session:1.8.0、androidx.media:media:1.7.0 |
| 签名 | android/key.properties,缺失时回退 debug 签名 |
| Capacitor 插件 | capacitor-app、capacitor-screen-orientation、capacitor-status-bar、nodejs-mobile-cordova |