本文章是基于Kotlin版的简单Activity-Fragment状态切换及数据保留的探讨,原问题如下:
页面长时间放置后台,或者在其他页面crash后,退到主页面,发现智护页面出现重叠。
该页面架构简单:主Activity对两个Fragment,一个智护Fragment,一个报告Fragment。
一、Activity创建时读取数据
正常情况下Activity的生命周期比较简单,这里就不详细叙述了,不清楚的可以自行百度~
Activity在创建的时候会执行生命周期的OnCreate方法,该方法的参数为Bundle类型。那么这个数据是从哪里得来的呢?
override fun onCreate(savedInstanceState: Bundle?) {
}
1、Activity如何保持数据
当横竖屏切换时,Activity会被销毁,生命周期方法onPause,onStop,onDestory等均会被调用,此时Activity属于异常情况下终止的,所以系统会调用onSaveInstanceState方法对Activity的状态进行保存。该方法在onStop之前调用,与onPause没有既定的时序关系。
当出现Crash时,除了其他的生命周期方法不会执行外,也会执行保存数据的操作。
当Activity被重新创建后,系统会调用onRestoreInstanceState,将之前onSaveInstanceState保存的数据Bundle传递给onRestoreInstanceState和onCreate方法,因此我们可以通过onRestoreInstanceState和onCreate方法判读Activity是否被重建了,如果被重建了,那么我们就可以取出之前保存的数据并进行恢复,onRestoreInstanceState的调用时机在onStart之后。
注意:正常情况下Activity的创建和销毁不会调用onSaveInstanceState和onRestoreInstanceState方法。
// 此方法用来保存当前异常退出的Activity的数据
override fun onSaveInstanceState(outState: Bundle?, outPersistentState: PersistableBundle?) {
super.onSaveInstanceState(outState, outPersistentState)
}
// 此方法用来重载当前异常退出的Activity的数据
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
super.onRestoreInstanceState(savedInstanceState)
}
我们只需要在onSaveInstanceState中保存需要存储的参数,然后再onRestoreInstanceState获取保存的数据并进行设置即可。
2、Activity恢复数据
OnCreate方法的Bundle类型参数,其实和onRestoreInstanceState中的Bundle参数是一样的,不过需要我们自己进行判断,而onRestoreInstanceState如果Bundle为Null时则不会调用。因此我们也可以对onCreate方法的Bundle参数进行判断,当Bundle不为null时对数据进行恢复。
2、如何防止Activity重建
1、设置Activity的方向固定
onCreate方法中设置 但是这种情况不适合手机已经设置为自动旋转的情况。需要在清单文件下设置
android:screenOrientation=“portrait”
//
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
2、Manifest清单设置
不仅仅当屏幕方向切换时会重建Activity,当系统配置发生改变的时候Activity都会被重建,例如用户插入外接键盘,运营商改变,界面模式(例如开启夜间模式)等都会导致Activity重建。如果我们不希望当系统配置发生变化界面重建,那么我们需要在AndroidManifest.xml中对Activity的configChange属性进行配置。例如我们不希望屏幕旋转时重建,则需要如下设置:
android:screenOrientation="portrait"
android:configChanges="orientation"
3、解决上述问题
因为Activity的重建,导致图中的问题出现。
解决方式一:
使用tag标记Fragment,再次使用的时候判断该Fragment是否销毁:
var monitorFragment = fragmentManager.findFragmentByTag("monitor")
monitorFragment?.let {
fragmentTransaction.show(it)
}?:let {
monitorFragment = MonitorFragment()
fragmentTransaction.add(R.id.fg_content,monitorFragment,"monitor")
}
解决方式二:
在Oncreate方法中,判断savedInstanceState是否为空,如果不为空,直接finish掉,直接让当前页面挂掉(实用于负责页面,复杂数据,避免引发更多的错误)。
override fun onCreate(savedInstanceState: Bundle?) {
if (savedInstanceState != null){
finish()
}
}
其实和Activity类似,Fragment也有onSaveInstanceState的方法,在此方法中进行保存数据,然后在onCreate或者onCreateView或者onActivityCreated进行恢复都可以。网上相关文章很多,学习不能停,如果你愿意,总能发现惊喜!