关于Android状态保持看这一篇就够了

Categories: android

最近好像蛮流行这样给文章取名的,恩,蛮叼的!其实文章内容是一个业务问题引发的关于状态保持的理解!

Fragment的状态保持

先说Fragment状态保持相关的问题,因为最近做项目碰到了问题。

遇到问题

问题大概是这样的,某一个页面中有一块内容是用Fragment显示的,有3个Fragment,用户可以通过点击切换按钮来切换这块内容显示哪个Fragment。这些Fragment中就会有很多业务内容,包括影响显示View当前情况的一些数据参数。如果用户在Fragment1中操作了之后切换到了Fragment2,然后又切换回来,这时候Fragment1中原有的状态就没了,又变成初始化的样子了。

这时候我首先想到要保持状态,于是然后想当然的就写下了如下的代码:

private lateinit var myDay1:Calendar
override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    val saveDay = savedInstanceState?.getSerializable(SAVE_MY_DAY_KEY)
    myDay1 = if (saveDay!=null) {
        saveDay as Calendar
    }else {
        Calendar.getInstance()
    }
}
override fun onSaveInstanceState(outState: Bundle?) {
     super.onSaveInstanceState(outState)
     outState?.putSerializable(SAVE_MY_DAY_KEY, myDay1)
 }

结果无效!

因为压根就没执行onSaveInstanceState这个方法,是的,其实这个方法是从Activity开始执行,Activity执行了这个onSaveInstanceState方法的时候,会把它内部的所有View,包括Fragment都执行一次它们的onSaveInstanceState。然而我这个Activity好好的,至始至终都没有离开过,所以不会执行这个方法。

那我这块业务怎么办呢,其实也很简单,因为业务本来就有切换Fragment的要求,所以这几个Fragment实例本来就在Activity中创建了,然后在切换的时候虽然Fragment销毁了,但是它实例还是在Activity中的,不会销毁,所以它内部的变量也还在的,那只要在初始化这个myDay1的时候判断它是否已经存在就可以了。

override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        if (!this::myDay1.isInitialized) {//判断是否已经初始化过
            val saveDay = savedInstanceState?.getSerializable(SAVE_MY_DAY_KEY)
            myDay1 = if (saveDay != null) {
                saveDay as Calendar
            } else {
                Calendar.getInstance()
            }
        }
    }

Fragment总结

其实上面说的情况,并不是大家平常说的状态保持,比如在操作的时候有电话进来了,或者其他啥情况切换到应用外面,然后回来的时候希望能保持原来离开时候的状态。而是一种业务上面的需求。但是上面的代码,不仅仅完成了这个业务,也能适应大家说的那种情况。

这里还有个说明,就是Fragment内部的View,如果这个Fragment在切换的时候添加到了堆栈addToBackStack,这时候Fragment的生命周期是到onDestroyView,然后显示的时候会重现创建View,但是你会发现这些View的状态还是保持了,虽然Fragment没有执行onSaveInstanceState方法,外面Activity也没有执行。其实Fragment本身没有销毁,它内部会对这些View自动保持状态和恢复状态。

View的状态保持

刚才上面Fragment那块说到了,保持状态的onSaveInstanceState方法是从Activity执行,并且Activity执行的时候会搜索每个View,所有有实现onSaveInstanceState方法的View都会执行,包括恢复状态的时候执行onRestoreInstanceState方法也是一样的。所以其实你看官方的那些View,基本都有实现这两个方法,你在用的时候,比如EditText,如果你在输入框写入了一些内容,切换回应用的时候这些内容还在。

如果你要写一个自定义的View,别忘了实现onSaveInstanceStateonRestoreInstanceState这两个方法。

override fun onSaveInstanceState(): Parcelable {
    super.onSaveInstanceState()
    val bundle = Bundle()
    //...保存数据
    return bundle
}

override fun onRestoreInstanceState(state: Parcelable?) {
    super.onRestoreInstanceState(state)
    //...恢复数据
}

PS:Activity查找这些实现了这两个方法的View,并且恢复的时候还原数据,用的都是android:id,所以只有写了android:id的View才有这个保持状态的效果。

Activity的状态保持

这个就简单了,只要实现onSaveInstanceStateonRestoreInstanceState两个方法就行了。

private var myStr = "hello"
override fun onSaveInstanceState(outState: Bundle?) {
	super.onSaveInstanceState(outState)
	outState?.putString("SaveState", myStr)
}

override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
	super.onRestoreInstanceState(savedInstanceState)
	val saveStr = savedInstanceState?.getString("SaveState")
	if (!TextUtils.isEmpty(saveStr)) myStr=saveStr
}