在SplashScreen的setKeepOnScreenCondition中调用setContent安全吗?
问题背景
我正在使用SplashScreen compat库实现应用启动页,技术栈同时包含Jetpack Compose(构建UI)、Jetpack Compose Navigation(实现导航逻辑)。
应用共两个页面:登录页、内容页,启动时需要根据用户登录状态跳转对应页面:
- 已登录用户:直接进入内容页
- 未登录用户:进入登录页
当前写的实现代码如下:
class MainActivity : ComponentActivity() { private var job: Job? = null private var keep = true override fun onCreate(savedInstanceState: Bundle?) { installSplashScreen().setKeepOnScreenCondition { if (job != null && keep) { return@setKeepOnScreenCondition true } if (!keep) { return@setKeepOnScreenCondition false } job = lifecycleScope.launch { val startDestination = if (isUserLoggedIn()) { // 耗时挂起方法,判断用户登录状态 "content" } else { "login" } setContent { NavHost(..., startDestination = startDestination) { composable(route = "login") { ... } composable(route = "content") { ... } } } keep = false } return@setKeepOnScreenCondition keep } super.onCreate(savedInstanceState) } }
疑问
上述代码在setKeepOnScreenCondition回调中调用setContent的写法是否安全?
回答
这种写法不安全,存在明确的运行隐患:
setKeepOnScreenCondition的回调会在主线程每一帧绘制前被高频调用,API设计上只要求这个回调做轻量的状态读取、返回布尔值,本身不支持在回调内执行任何副作用逻辑(比如启动协程、调用setContent、修改可变状态)。框架没有对回调内的副作用做重复调用防护、时序保证,很容易因为生命周期重建(比如屏幕旋转、系统恢复页面)、标志位时序问题,触发协程重复启动、setContent重复调用,轻则导航栈状态错乱,重则直接抛异常崩溃。- 你当前加的
job判空、keep标志位逻辑只能覆盖最理想的首次启动场景,一旦出现异常生命周期流转,很容易出现启动页无法正常退出、首帧加载卡住的问题。
推荐实现方式
把副作用逻辑和启动页判断逻辑完全拆分,符合API的设计约定:
- 安装SplashScreen时,
setKeepOnScreenCondition里只做纯布尔值返回,不写任何其他逻辑 - 登录状态判断、Compose视图初始化这类逻辑,放在
onCreate的主执行流程里 - 等首帧UI准备完成后,再修改标志位退出启动页
参考实现代码:
class MainActivity : ComponentActivity() { // 启动页保活标志,默认保持展示 private var keepSplashScreen = true override fun onCreate(savedInstanceState: Bundle?) { // 安装启动页,回调仅做纯判断,无任何副作用 installSplashScreen().setKeepOnScreenCondition { keepSplashScreen } super.onCreate(savedInstanceState) // 主流程执行初始化逻辑 lifecycleScope.launch { // 异步判断登录状态 val startDestination = if (isUserLoggedIn()) "content" else "login" // 挂载Compose视图 setContent { NavHost(..., startDestination = startDestination) { composable(route = "login") { /* 登录页UI */ } composable(route = "content") { /* 内容页UI */ } } } // UI准备完成,退出启动页 keepSplashScreen = false } } }
这种写法既可以保证启动页一直展示到首帧UI准备完成,不会出现提前退出露白的问题,也完全规避了回调内执行副作用带来的重复调用、状态错乱风险,稳定性更高。
内容的提问来源于stack exchange,提问作者Good
相关产品推荐
相关产品推荐

