如何解决Android 12 SplashScreen结束前Fragment提前初始化问题
问题根因
OnPreDrawListener 仅拦截界面绘制流程,不会阻塞布局加载、Fragment初始化的执行逻辑。调用setContentView时,布局中声明的NavHostFragment会立刻被实例化,从onAttach到onViewCreated的生命周期链路会正常执行,和闪屏是否展示、是否挂起绘制没有关联,必然会出现时序错位。
修复方案
方案1:延迟绑定导航图(改动最小)
- 第一步:修改
activity_main布局,移除FragmentContainerView上预配置的app:navGraph属性,不在布局加载阶段绑定导航图:
<androidx.fragment.app.FragmentContainerView android:id="@+id/nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="match_parent" android:layout_height="match_parent" app:defaultNavHost="true" />
- 第二步:调整闪屏等待逻辑,仅在初始数据加载完成后,再手动给NavHostFragment设置导航图——设置navGraph的动作才会触发起始Fragment的创建和生命周期回调:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) installSplashScreen() setContentView(R.layout.activity_main) val content: View = findViewById(android.R.id.content) // 此时仅实例化空的NavHostFragment,不会加载任何业务Fragment val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController content.viewTreeObserver.addOnPreDrawListener( object : ViewTreeObserver.OnPreDrawListener { override fun onPreDraw(): Boolean { return if (mainActivityViewModel.isDataReady()) { Timber.tag("Splash").d("data ready") // 数据就绪后再绑定导航图,触发业务Fragment初始化 if (navController.currentDestination == null) { navController.setGraph(R.navigation.nav) } content.viewTreeObserver.removeOnPreDrawListener(this) true } else { Timber.tag("Splash").d("data not ready") false } } } ) }
代码中加currentDestination == null判断是为了避免Activity重建(旋屏、系统后台回收)时重复设置导航图,引发状态异常。
方案2:延迟添加NavHostFragment(隔离更彻底)
如果不希望在数据加载阶段实例化任何导航相关组件,可以把activity_main初始布局设为不带FragmentContainerView的空根容器,等数据加载完成后,再通过FragmentTransaction动态添加NavHostFragment并绑定导航图。这种方案能做到数据就绪前完全不触发任何业务Fragment的生命周期,适合初始化逻辑重、对启动时序要求极高的场景。
避坑提示
不要在Fragment生命周期中加主线程阻塞逻辑等待数据,这类操作会卡住主线程,轻则触发ANR,重则导致Fragment状态保存/恢复异常。所有依赖启动初始数据的Fragment逻辑,必须保证在数据就绪后再触发初始化。
内容的提问来源于stack exchange,提问作者hushed_voice
相关产品推荐
相关产品推荐

