Android-纯H5页面项目踩坑记录

问题一:

App 内嵌入了一个 H5 单页应用。用户反馈:App 切到后台随便打开另一个 App 后,再切回我们的 App 时,H5 页面会重新加载,而不是停留在用户切后台前浏览的页面。

更诡异的是:从多任务键(Recent Apps)切回去没问题,只有点击桌面图标切回去才会重新加载。


定位两种切回方式的差异

切回方式 触发的生命周期 Activity 是否重建
多任务键切换 onRestart()onStart()onResume() ❌ 不重建
点击桌面图标 onCreate()initView() → ... ✅ 全量重建

点击桌面图标时 SplashActivity (启动页)被重新创建了。


根因分析

App 的启动流程

复制代码
AndroidManifest.xml
┌─────────────────────────────────────────┐
│ SplashActivity                          │
│   intent-filter: MAIN + LAUNCHER        │
│   launchMode: 默认(standard)            │
│                                         │
│   onCreate() {                          │
│     loadData()        // 业务逻辑         │
│     MainActivity      // 跳转主页        │
│     finish()         // 启动完就销毁自己   │
│   }                                     │
└─────────────────────────────────────────┘

正常冷启动流程:

复制代码
[启动] → [SplashActivity] → [MainActivity(含 WebView)] → SplashActivity finish()
任务栈变为: [MainActivity]

点击桌面图标的实际行为

Android Launcher 发现目标 App 已有任务栈在后台运行,但 SplashActivity 声明了 MAIN + LAUNCHERlaunchMode="standard",系统会:

  1. 重新创建一个新的 SplashActivity 实例,压在已有栈的顶部
  2. 新 SplashActivity 走完整初始化 → MainActivity.open() → 又创建一个新的 MainActivity

结果:

java 复制代码
[旧 MainActivity(WebView 完好)] → [新 SplashActivity] → [新 MainActivity(WebView 重新加载)]
                                                                         ↑
                                                                  用户看到的是这个

旧 MainActivity 的 WebView 状态完好(按返回键甚至还能看到它),但用户被新 Activity 挡住了视线。

这就是「多任务切换不重载、点图标重载」的根本原因。


补充:为什么有的手机没这个问题?

测试中发现绝大多数手机点击图标都能正常回到原界面,只有一台手机必现。这是因为 Android 没有强制规定 Launcher 启动 App 的行为

不同厂商/桌面的实现差异:

正常手机(大多数):

Launcher 检测到目标 App 后台已有任务栈 → 使用 moveTaskToFront 或带上 FLAG_ACTIVITY_BROUGHT_TO_FRONT 的 Intent → 直接把已有栈拉到前台,不创建 Activity

问题手机:

Launcher 不做已有栈检测,直接用 ACTION_MAIN + CATEGORY_LAUNCHER 裸发 Intent → 系统按默认 standard 模式创建新的 SplashActivity → 触发重载。

常见的触发场景:

  • 部分厂商魔改的系统桌面(某些 ROM 特定机型)
  • 用户安装了第三方桌面(Nova Launcher、微软 Launcher 等)
  • 开发者选项中开启了「不保留活动」(Don't keep activities)
  • 手机开启了「应用分身」「双开」等特性

这也恰好印证了方案一(isTaskRoot)比方案二(singleTask)更合适------它是防御式检查,正常手机走不到这个分支、零影响;问题手机命中分支、一口兜住。


解决方案

方案一:isTaskRoot 判断(推荐)

SplashActivity.onCreate() 最前面加一行判断:

kotlin 复制代码
// SplashActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
    // 如果自己不是任务栈根节点,说明 App 已在运行
    if (!isTaskRoot) {
        finish()
        return
    }
    // ... 原有初始化逻辑 ...
}

原理:

  • isTaskRoot 判断当前 Activity 是不是所在任务栈的栈底(第一个 Activity)
  • 正常冷启动:SplashActivity 是栈底 → isTaskRoot = true → 走正常流程
  • 后台切回点了图标:已存在栈 [TaskDetailActivity],新 SplashActivity 不是栈底 → isTaskRoot = false → 直接 finish,露出旧栈
场景 isTaskRoot 行为
首次冷启动 true 正常初始化
进程被杀死后重启 true 正常初始化
后台存活 + 点图标 false 直接 finish,回到旧栈

优点: 3 行代码,意图明确,不改变 Manifest,不影响其他流程。

方案二:singleTask 启动模式

AndroidManifest.xml 中为 SplashActivity 添加:

xml 复制代码
<activity
    android:name=".ui.SplashActivity"
    android:launchMode="singleTask"
    ... />

原理: singleTask 模式下,如果任务栈已存在,系统不会创建新实例,而是把已有任务拉到前台并调用 onNewIntent()

缺点:

  • 需要在 onNewIntent() 中处理业务等逻辑
  • 改动面更大,需要回归测试认证流程
  • 行为变更隐式依赖系统,可读性不如方案一

方案三:同时增加 WebView 状态持久化(兜底)

以上方案解决 Activity 不被重建的问题。但进程仍可能被系统杀死。作为兜底,可增加 WebView 状态持久化:

kotlin 复制代码
// MainActivity.kt
override fun onStop() {
    super.onStop()
    // 保存 WebView 状态到文件
    val stateFile = File(filesDir, "webview_state")
    val stateBundle = Bundle()
    mBind.webView.saveState(stateBundle)
    stateFile.writeBytes(stateBundle.toByteArray())
    // 备用:保存当前 URL 到 SharedPreferences
    prefs.edit().putString("last_url", mBind.webView.url).apply()
}

// onCreate() 中
if (savedInstanceState != null) {
    // 恢复 WebView 状态
    restoreWebViewState()
    return  // 不执行 loadUrl()
}

总结

层面 问题 措施 优先级
Activity 栈管理 点图标启动新 SplashActivity isTaskRoot 判断,非栈底直接 finish P0
singleTask 启动模式 需处理业务逻辑,改动面大 在newIntent中做好处理 P1
进程死亡兜底 系统杀进程后 WebView 状态丢失 saveState/restoreState 持久化 P2

三个措施,推荐第一个,可以解决 H5 页面的重新加载问题。

相关推荐
凛_Lin~~3 天前
lifecycle源码解析 (版本2.5.1)
android·java·安卓·lifecycle
꯭爿꯭巎꯭4 天前
澎湃工具箱下载v3.8.9 (官网版)澎湃系统工具箱
android·智能手机·安卓
humors2215 天前
全平台日常使用的国外应用
android·ios·app·安卓·应用·国外
装杯让你飞起来啊8 天前
第 4 周 Unit 2:Jetpack Compose 状态、按钮、计数器与小费计算器
windows·microsoft·kotlin·安卓
꯭爿꯭巎꯭10 天前
root环境检测软件(环境检测工具大全root )Nativetest
智能手机·安卓
꯭爿꯭巎꯭10 天前
汽水音乐车机版下载(汽水音乐车载版hd)
安卓
꯭爿꯭巎꯭11 天前
玄戒工具箱下载最新版3.4
智能手机·安卓
北京自在科技15 天前
Find Hub App 小更新
android·ios·安卓·findmy·airtag
suki_lynn15 天前
云手机应用场景和实际用途
智能手机·云计算·手机·安卓