Fragment引起的血案
前几天遇到一个很奇怪的问题,主页Fragment的onResume中的接口,存在偶现的频繁刷接口的现象。
看上去代码一切正常,我们也重现不出来,但是线上就是存在刷接口的现象,被我们运维频频吐槽。
在百思不得其解之际,终于找到突破口 : 自己对于Fragment的使用一直存在误区。
Fragment的使用误区
Fragment在Activity旋转屏幕或重建后,并不会销毁,而是存储在saveInstanceState中。
先附上我们平时对于Fragment的用法,( 这是错误的 )
如果Activity发生了重建,这会导致内存中,存在两份Fragment,
甚至于,该Activity多次重建后,就会有多个Fragment同时存在,
而这些Fragment都会重新走生命周期,导致生命周期中的接口被多次频繁调用。
getSupportFragmentManager().beginTransaction()
.add(R.id.layout_container, SecondFragment.newInstance(), "SecondFragment")
.commit();
方案一
Fragment的show和add方法应该结合使用,如果能够找到对应的tag的fragment,应该要show Fragment,而不是新创建一个fragment来add
正确用法:
Fragment fragment = getSupportFragmentManager().findFragmentByTag("SecondFragment");
if (fragment != null) {
getSupportFragmentManager().beginTransaction()
.show(fragment)
.commit();
}else{
getSupportFragmentManager().beginTransaction()
.add(R.id.layout_container, SecondFragment.newInstance(), "SecondFragment")
.commit();
}
方案二
在所有fragment的使用中,都判断show和add,相对比较繁琐,如果对于fragment重用没有要求,那么我们完全可以禁止fragment的缓存,从而一劳永逸,解决这个问题。
在Activity的onSaveInstanceState,加入如下代码
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// 只要发生onSaveInstanceState就remove all Fragment
if (outState != null) {
Log.i(TAG, "outState.remove");
outState.remove("android:support:fragments");
}
}
方案三
如果我们对大厂的App们进行测试,会发现一个惊人的秘密,大厂的App完全禁止了App的SaveInstanceState,即不用再考虑App销毁重建时,数据的存储恢复情况,这不仅仅可以避免fragment的问题,还可以避免因遗漏对某些数据进行存储/恢复处理,导致的异常情况的发生。
当然这也有代价,那就是系统回收了App后,无法保存现场页面状态和数据。
@Override
public void onSaveInstanceState(Bundle outState) {
//注释掉这行
//super.onSaveInstanceState(outState);
}
为什么我们的Activity会发生重建
我们的app的旋转方向限定为竖屏,理论上不会发生Activity重建的情况。
那是什么场景导致Activity重建呢 ? 搜寻资料后发现
在 Android 的 API 21 ( Android 5.0 ) 以下,Crash 会直接退出应用,但是在 API 21 ( Android 5.0 ) 以上,系统会遵循以下原则进行重启:
- 包含 Service,如果应用 Crash 的时候,运行着Service,那么系统会重新启动 Service。
- 不包含 Service,只有一个 Activity,那么系统不会重新启动该 Activity。
- 不包含 Service,但当前堆栈中存在两个 Activity:Act1 -> Act2,如果 Act2 发生了 Crash ,那么系统会重启 Act1。
- 不包含 Service,但是当前堆栈中存在三个 Activity:Act1 -> Act2 -> Act3,如果 Act3 崩溃,那么系统会重启 Act2,并且 Act1 依然存在,即可以从重启的 Act2 回到 Act1。
国内有些厂商可能会去除这个功能
所以,我们有时候出现崩溃,会发现App并没有闪退,而是会恢复到上一个页面 (上一个页面进行了重建)。
小结
至此,我们知道了Fragment的使用中,add/show的误区,并给出了解决方案。
并说明了Activity重建的情况,扩展了自己的认知边界。
在日后的工作中,我们应该要避免Fragment错误使用的情况。
最后,附上用来重现该问题的Demo