WebToApp 启动闪屏(Splash)配置指南:从图片/视频选择、倒计时与跳过逻辑到 APK 打包加密
本篇指南讲解 WebToApp 项目中「编辑通用配置(Edit Common Config)」里的Splash Screen(启动闪屏)功能:如何在应用启动时展示一张图片或一段视频,并精细控制展示时长、点击跳过、倒计时、横竖屏方向、裁剪适配、视频音频与裁剪区间,以及这些配置如何被打包进导出的 APK(含可选加密)。读完本文,你将掌握闪屏功能在编辑器中的全部选项、底层配置字段(splashEnabled/splashConfig)与运行期实现原理,并能在自己的应用上直接复现配置。
功能入口与整体概念
闪屏是应用启动瞬间展示给用户的第一屏,常用于品牌露出、过渡缓冲或承载激活/公告流程。在 WebToApp 中,该功能位于编辑通用配置(Edit Common Config)编辑器内的Splash screen(启动闪屏)卡片中(相关文档见 splash.md)。
从数据模型看,每个应用(WebApp)通过两个字段控制闪屏(见 WebApp.kt):
val splashEnabled: Boolean = false, val splashConfig: SplashConfig? = null,splashEnabled:闪屏总开关;splashConfig:完整的闪屏行为配置,为null时视为未启用。
对应的SplashConfig定义在 WebApp.kt:
data class SplashConfig( val type: SplashType = SplashType.IMAGE, // IMAGE / VIDEO val mediaPath: String? = null, // 闪屏媒体文件路径 val duration: Int = 3, // 展示时长(秒),图片闪屏使用 val clickToSkip: Boolean = true, // 是否允许点击跳过 val orientation: SplashOrientation = SplashOrientation.PORTRAIT, val fillScreen: Boolean = true, // 裁剪填充 vs 完整适配 val enableAudio: Boolean = false, // 视频闪屏是否播放声音 val videoStartMs: Long = 0, // 视频裁剪起点(毫秒) val videoEndMs: Long = 5000, // 视频裁剪终点(毫秒) val videoDurationMs: Long = 0, // 视频总时长(毫秒,编辑器裁剪用) val showCountdown: Boolean = true // 是否显示剩余秒数倒计时 )配套枚举SplashType(IMAGE/VIDEO)与SplashOrientation(PORTRAIT/LANDSCAPE)也定义在同一文件中。
编辑器选项逐项说明
编辑器卡片实现在 CreateAppSplashCard.kt,对应文档中的全部选项如下:
| 选项 | 对应配置字段 | 说明 |
|---|---|---|
| Enable(启用) | splashEnabled | 闪屏总开关。关闭时整张卡片折叠,相关配置保留但不再生效。 |
| Media(媒体) | splashConfig.mediaPath/type | 选择图片(image/*)或视频(video/*)。卡片提供「选择图片」「选择视频」两个按钮,选中后展示预览(图片显示缩略图,视频显示裁剪控件)。 |
| Duration(时长) | splashConfig.duration | 图片闪屏的展示秒数,编辑器滑块范围为1~5 秒(valueRange = 1f..5f,默认 3)。 |
| Click to skip(点击跳过) | splashConfig.clickToSkip | 允许用户点击屏幕直接跳过闪屏。 |
| Show countdown(显示倒计时) | splashConfig.showCountdown | 独立控制在闪屏右上角显示剩余秒数,与点击跳过互不影响。 |
| Orientation(方向) | splashConfig.orientation | 切换横屏(LANDSCAPE)或竖屏(PORTRAIT)展示闪屏。 |
| Fill screen(铺满屏幕) | splashConfig.fillScreen | true时按裁剪(Crop)铺满全屏,false时按完整适配(Fit)显示。 |
| Enable audio(启用音频) | splashConfig.enableAudio | 仅视频闪屏可见,控制视频是否带声音播放(默认静音)。 |
| Video trim(视频裁剪) | splashConfig.videoStartMs/videoEndMs | 为视频闪屏设置起止裁剪区间(毫秒)。编辑器内置VideoTrimmer控件,并记录videoDurationMs供滑块参考。 |
| Clear media(清除媒体) | — | 移除已选闪屏媒体,回到选择态。 |
几个值得注意的交互细节(均可从 CreateAppSplashCard.kt 验证):
- 卡片会校验媒体文件是否存在(
checkMediaExists),若所选 URI 对应的文件已被删除,会自动触发onClearMedia清空选择; - 「显示时长」「启用音频」等选项仅在对应媒体类型下显示(图片类型不显示时长以外的视频选项,视频类型不显示时长滑块);
- 「横屏显示」开关本质是把
orientation在LANDSCAPE与PORTRAIT之间切换。
媒体选择后的落盘处理
媒体并不直接引用系统相册 URI,而是先复制进应用私有目录。相关实现见 SplashStorage.kt:
- 视频:以
splash_<uuid>.mp4命名,通过openInputStream+ 缓冲拷贝保存; - 图片:先按
MAX_IMAGE_SIZE = 1920计算采样比例(calculateInSampleSize),超限时等比缩放,最终以 PNG 格式保存; - 支持的扩展名:视频
mp4 / webm / 3gp / mkv / avi / mov,图片png / jpg / jpeg / gif / webp / bmp; - 文件统一存放在
filesDir/splash_media/目录,并提供deleteMedia、cleanupUnusedMedia、getStorageStats等管理接口。
闪屏在导出 APK 中的运行流程
导出阶段:打包与可选加密
导出 APK 时,闪屏媒体会被嵌入应用资源。核心逻辑在 ApkBuilder.kt 的addSplashMediaToAssets:
- 媒体以固定资源名写入 APK:图片为
assets/splash_media.png,视频为assets/splash_media.mp4; - 若启用资源加密(
encryptionConfig.enabled),则写入assets/splash_media.<ext>.enc加密文件; - 超过10 MB的视频走专门的流式处理分支:加密模式下用
encryptLargeFile,非加密模式下用writeEntryStoredStreaming,避免一次性读入内存; - 写入前会校验文件存在、可读且非空,否则跳过并记录日志。
导出配置块由buildSplashBlock()生成(ApkBuilder.kt),将splashEnabled、type、duration、clickToSkip、videoStartMs、videoEndMs、landscape、fillScreen、enableAudio、showCountdown全部序列化进 APK 的配置 JSON。辅助函数getSplashMediaPath()(ApkBuilder.kt)保证只有splashEnabled == true时才返回媒体路径。
运行阶段:导出应用的启动闪屏
导出后的应用通过 SplashLauncherActivity.kt 承载启动闪屏,其关键行为:
- 通过
AppModifyPayload接收闪屏配置,仅当splashEnabled、mediaPath非空且文件真实存在时展示闪屏,否则直接拉起目标页; - 若
orientation == LANDSCAPE,在onCreate中强制SCREEN_ORIENTATION_LANDSCAPE; - 图片闪屏:
LaunchedEffect(countdown)每秒递减,倒数到 0 后结束闪屏并启动目标 Activity;点击跳过(clickToSkip)会立即结束; - 视频闪屏:使用
MediaPlayer+SurfaceView播放,seekTo(videoStartMs)后播放到videoEndMs暂停并结束;音频按enableAudio设置音量 1f 或 0f;代码对「返回键打断播放」「播放器已释放」等竞态做了防御(注释引用 issue #612); - 倒计时芯片:视频模式显示剩余毫秒向上取整的秒数,图片模式显示
countdown数值,同时展示可选的「Skip」文字,整个芯片固定在右上角(Alignment.TopEnd)。
此外,在 WebToApp 自身的 WebView 壳/预览场景中,同样有一套 Compose 覆盖层 WebViewSplashOverlay.kt,行为与导出版一致:
fillScreen直接映射为ContentScale.Crop(裁剪铺满)或ContentScale.Fit(完整适配);- 背景固定为黑色
Color.Black; - 视频循环关闭(
isLooping = false),播放到videoEndMs后回调onComplete; - 芯片使用
statusBarsPadding(),这正是文档 Notes 中「状态栏可见时倒计时/跳过芯片自动位于状态栏下方,纯全屏时停留在顶部角落」的实现来源。
启动流程中的优先级:激活 → 公告 → 闪屏
值得说明的是,闪屏并非导出应用启动流程的唯一环节。从 SplashLauncherActivity.kt 可以确认启动顺序:
- 若启用激活(
activationEnabled),先做本地/远程激活校验,未激活时弹出激活对话框; - 若启用公告(
announcementEnabled)且满足展示条件,先弹公告,关闭后再进入闪屏; - 最后才展示闪屏,结束后拉起目标 Activity(
onLaunchTarget)。
理解这一顺序有助于排查「闪屏不出现」的问题:它可能只是被激活或公告流程挡在了前面。
数据落库与测试佐证
WebApp是 Room 实体(表web_apps),splashConfig等嵌套配置通过 Converters.kt 序列化存储,因此闪屏配置随应用数据持久化。在测试侧,EditStateMapperTest.kt 验证了SplashConfig(type = SplashType.IMAGE, mediaPath = "file:///splash.png")能被正确映射为编辑态的splashMediaUri,即「编辑器选择媒体 → 编辑态 URI → 落盘路径」这条链路是有测试保障的。
小结与实用建议
综合以上内容,可以提炼出几条直接可用的实操建议:
- 图片闪屏:设置 1~5 秒时长(默认 3 秒),打开「点击跳过」避免用户等待过长;「显示倒计时」可与跳过并存,右上角会呈现
Ns | Skip芯片。 - 视频闪屏:通过「视频裁剪」把展示片段限定在
videoStartMs~videoEndMs(默认整段取前 5 秒),超 10 MB 的视频在导出时会走流式写入,加密开启后仍受支持;默认静音,需要配音时打开「启用音频」。 - 适配与方向:
fillScreen = true时媒体按 Crop 裁剪铺满、无黑边但有裁切风险;false时完整显示但可能留黑边。横屏应用记得把闪屏方向同步设为LANDSCAPE。 - 打包验证:导出后可在 APK 的
assets/下看到splash_media.png或splash_media.mp4(开启资源加密时为.enc文件),这是确认闪屏媒体是否成功嵌入的最直接方法。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考