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声明了MAINLAUNCHER且launchModestandard系统会重新创建一个新的 SplashActivity 实例压在已有栈的顶部新 SplashActivity 走完整初始化 →MainActivity.open()→ 又创建一个新的 MainActivity结果[旧MainActivityWebView完好]→[新SplashActivity]→[新MainActivityWebView重新加载]↑ 用户看到的是这个旧 MainActivity 的 WebView 状态完好按返回键甚至还能看到它但用户被新 Activity 挡住了视线。这就是「多任务切换不重载、点图标重载」的根本原因。补充为什么有的手机没这个问题测试中发现绝大多数手机点击图标都能正常回到原界面只有一台手机必现。这是因为Android 没有强制规定 Launcher 启动 App 的行为。不同厂商/桌面的实现差异正常手机大多数Launcher 检测到目标 App 后台已有任务栈 → 使用moveTaskToFront或带上FLAG_ACTIVITY_BROUGHT_TO_FRONT的 Intent → 直接把已有栈拉到前台不创建 Activity。问题手机Launcher 不做已有栈检测直接用ACTION_MAINCATEGORY_LAUNCHER裸发 Intent → 系统按默认standard模式创建新的 SplashActivity → 触发重载。常见的触发场景部分厂商魔改的系统桌面某些 ROM 特定机型用户安装了第三方桌面Nova Launcher、微软 Launcher 等开发者选项中开启了「不保留活动」Don’t keep activities手机开启了「应用分身」「双开」等特性这也恰好印证了方案一isTaskRoot比方案二singleTask更合适——它是防御式检查正常手机走不到这个分支、零影响问题手机命中分支、一口兜住。解决方案方案一isTaskRoot判断推荐在SplashActivity.onCreate()最前面加一行判断// SplashActivity.ktoverridefunonCreate(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 添加activityandroid:name.ui.SplashActivityandroid:launchModesingleTask.../原理singleTask模式下如果任务栈已存在系统不会创建新实例而是把已有任务拉到前台并调用onNewIntent()。缺点需要在onNewIntent()中处理业务等逻辑改动面更大需要回归测试认证流程行为变更隐式依赖系统可读性不如方案一方案三同时增加 WebView 状态持久化兜底以上方案解决 Activity 不被重建的问题。但进程仍可能被系统杀死。作为兜底可增加 WebView 状态持久化// MainActivity.ktoverridefunonStop(){super.onStop()// 保存 WebView 状态到文件valstateFileFile(filesDir,webview_state)valstateBundleBundle()mBind.webView.saveState(stateBundle)stateFile.writeBytes(stateBundle.toByteArray())// 备用保存当前 URL 到 SharedPreferencesprefs.edit().putString(last_url,mBind.webView.url).apply()}// onCreate() 中if(savedInstanceState!null){// 恢复 WebView 状态restoreWebViewState()return// 不执行 loadUrl()}总结层面问题措施优先级Activity 栈管理点图标启动新 SplashActivityisTaskRoot判断非栈底直接 finishP0singleTask启动模式需处理业务逻辑改动面大在newIntent中做好处理P1进程死亡兜底系统杀进程后 WebView 状态丢失saveState/restoreState持久化P2三个措施推荐第一个可以解决 H5 页面的重新加载问题。