Activity重建之殇
很多时候,对于一些初学者来说,activity的重建是很痛苦的,面对网络上众多的说法,我们无从下手,无从做起。
其实很多时候只要我们掌握了其中的必要方法,就能解决大部分的activity的重建问题。下面我就来总结一下activity重建的一些必要方法,以及列举几点常见的问题。
1. Activity何时需要重建
activity的销毁:
正常:正常情况下的销毁,比如用户按下Back按钮或者是activity自己调用了finish()方法
异常:异常情况下的销毁,或者出现内存不足的情况下而销毁。简单来说就是在“未经许可”的情况下被销毁了
异常销毁后,Activity会重新创建一个新的对象,而不再是以前的Activity
2. 如何重建
onSaveInstanceState()方法的覆写
为什么?
Android应用框架中定义的几乎所有UI控件都恰当的实现了onSaveInstanceState()方法, 因此当activity被摧毁和重建时, 这些UI控件会自动保存和恢复状态数据
但是很多时候我们需要保存更多的数据才能满足我们的重建,这时候我们就可以覆写onSaveInstanceState()方法保存一些保存瞬态数据,比如成员变量等。
当Activity在非正常情况下销毁时,会在onPause或onStop方法之前调用onSaveInstanceState方法,正常情况下不会调用
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("testStr", mString);
outState.putInt("testInt", mInt);
}
恢复Activity的状态
当Activity被销毁后要重建时,可以从onSaveInstanceState方法中保存的Bundle对象中恢复数据。onCreate()和onRestoreInstanceState()两个方法的Bundle对象保存的数据是一样的。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
mString = savedInstanceState.getString("testStr");
mInt = savedInstanceState.getInt("testInt");
}
}
或者使用onRestoreInstanceState,只有在Activity重建恢复数据时才会调用onRestoreInstanceState方法,正常情况下不会调用,所以不需要判断Bundle是否为空
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mString = savedInstanceState.getString("testStr");
mInt = savedInstanceState.getInt("testInt");
}
3. Fragment如何重建
Fragment重建跟Activity重建大同小异,方法差不多,下面是我在Fragment重建过程中遇到的一些问题:
Activity销毁前hide的Fragment在重建之后全部出现
原因:
经过项目应用跟自己写的demo测试发现,在Activity销毁之前hide到栈中的Fragment在重建之后mHidden全部为false,也就是说隐藏的Fragment不再隐藏,所以我们就看到了全部展现出来的样子
解决办法:
通过Activity或者其所在的父Fragment管理控制Fragment隐藏,但由于无法知道Activity重建时Fragment何时重建好,所以目前的做法是在Activity中监听Fragment是否重建好了
Fragment重建之后出现重叠(出现两个相同的Fragment)
原因:
由于Activity重建的几率比较小,所以很多开发者可能不会注意到Fragment重建后出现的问题。一般情况下,Fragment的创建都是在Activity的onCreate或onStart里面,当Activity重建时导致再次创建了Fragment,而重建的Fragment也出现了,所以就出现了重叠。解决办法:
复用重建的Fragment或者删掉重建的重新创建:
重建的Fragment上面的item无法点击
原因:
原因很简单,如果listener是从别的地方传进来的,当Fragment重建时如果没有保存listener,必然的会无法监听点击事件解决办法:
目前想到的方法有两种:- 在前面所说的那个监听Fragment重建完成的回调里传listener进去
- 在onAttach方法里面把Activity强制转化为listener
重建的特殊的Fragment状态异常
原因:
提出这个状态异常主要是针对项目里遇到的滑块无法滑动的问题。原因是Fragment重建时需要重新走一遍onBindViewHolder,所以通过findViewHolderAdaterByPosition是无法找到数据的解决办法:
通过前面说的onSaveInstanceState方法保存position,在onBindViewHolder时恢复状态
4. 建议及注意事项
一般情况下很难复现Activity销毁的情况,如果我们要测试可以通过开启“不保留活动”,如图:
如果不需要重建Activity或者Activity里面装载的Fragment比较多,要恢复到原来的状态比较困难,可以重写Activity或Fragment的onSaveInstanceState方法,如图:
5. Commit与commitAllowingStateLoss的区别:
在这里我觉得还有必要补充一下commit和commitAllowingStateLoss的区别:
commit方法和commitAllowingStateLoss方法,都同时调用了commitInternal方法,只是传的参数略有不同
在对 commit和commitAllowingStateLoss的传参进行判断后,将任务扔进activity的线程队列中。这个两个方法区别就在传参判断后的处理方法checkStateLoss
当使用commit方法时,系统将进行状态判断,如果状态(mStateSaved)已经保存,将发生”Can not perform this action after onSaveInstanceState”错误。如果mNoTransactionsBecause已经存在,将发生”Can not perform this action inside of ” + mNoTransactionsBecause错误
避免异常抛出的最优方法就是避免在异步回调方法中调用commit
以上就是我对activity重建的一些见解,希望对大家有帮助,如有写得不对的地方,欢迎指出。