Skip to content

原生插件

Android 版的原生能力由 5 个自研 Capacitor 插件 + nodejs-mobile 内嵌 Node 运行时 + 2 个 Rust workspace 包 组成。JS 侧统一通过 @capacitor/coreregisterPlugin 声明,位于 src/plugins/

插件总览

插件名职责原生实现
AndroidNativePlayback播放引擎、MediaSession、桌面歌词、频谱playback/PlaybackManager.java
AndroidDownloadSAF 目录授权、下载、本地音乐扫描download/AndroidDownloadPlugin.java
AndroidCache音频 / 歌词 / 封面 / 列表缓存与 LRU 清理cache/CacheStorage.java
AndroidLocalLyric本地歌词目录扫描与匹配lyric/AndroidLocalLyricPlugin.java
AndroidShare歌词海报保存到相册 / 系统分享AndroidSharePlugin.java

插件在 MainActivity.java 中注册;AndroidManifest.xml 声明所需权限与两个前台服务:

  • .playback.PlaybackServiceMediaSessionService,mediaPlayback 类型)
  • .playback.FloatingLyricService(桌面歌词悬浮窗)

所需权限:INTERNETWAKE_LOCKFOREGROUND_SERVICEFOREGROUND_SERVICE_MEDIA_PLAYBACKPOST_NOTIFICATIONSSYSTEM_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
visualizerData256 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 为 ExoPlayer SimpleCache 目录,参与全局 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.tswindow.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
minSdk29(Android 10)
compile / targetSdk36
ABI 分包armeabi-v7a / arm64-v8a / x86 / x86_64(不产 universal 包)
媒体库androidx.media3:media3-exoplayer:1.8.0media3-session:1.8.0androidx.media:media:1.7.0
签名android/key.properties,缺失时回退 debug 签名
Capacitor 插件capacitor-appcapacitor-screen-orientationcapacitor-status-barnodejs-mobile-cordova

基于 AGPL-3.0 许可发布