记录一次关于Activity与Fragment生命周期引起的异常

来源:互联网 发布:mac和iphone铃声 编辑:程序博客网 时间:2024/04/30 00:26

在Activity和Frgment生命周期中对于数据保存应该是大部分都能保存的,像按Home返回到后台,再切换回来后应该不会出大问题的,但一次内存过底把保存的Activity给Destroy后引起了错误,具体是Activity里Fragment A里有一个Fragment B, Fragment B 按理是在A的onCreate里进行网络请求后异步创建,那么Activity销毁后走OnCreate应该不会有什么问题。

但是呢,Fragment B报空指针。这便引出这个文章。

Activity & Fragment

介绍文章直接有官方网站地址如下:

https://developer.android.com/guide/components/fragments.html
https://developer.android.com//guide/components/activities.html
这里贴上重要的生命周期图

activity_lifecycle

instance_restore

fragment_lifecycle

activity_fragment_lifecycle

上面的图是初学者应该了解的,但Android开发一段时间后也应该复习一下。具体情景是DetailActivity里replace一个framelayout后添加的fragment A, 在fragment A里再使用相同方式添加 fragment B, 但使用的是fragmentManager 是调用 A的getChildFragmentManager()

正常情况下看打印的数据

05-24 17:24:18.584 22965-22965/com.yorkyu.fragmentlifedemo D/DetailActivity: onCreate: 05-24 17:24:18.586 22965-22965/com.yorkyu.fragmentlifedemo D/AFragment: onAttach: 05-24 17:24:18.586 22965-22965/com.yorkyu.fragmentlifedemo D/AFragment: onCreate: 05-24 17:24:18.587 22965-22965/com.yorkyu.fragmentlifedemo D/AFragment: onCreateView: 05-24 17:24:18.622 22965-22965/com.yorkyu.fragmentlifedemo D/AFragment: onViewCreated: 05-24 17:24:18.622 22965-22965/com.yorkyu.fragmentlifedemo D/AFragment: onActivityCreated: 05-24 17:24:18.622 22965-22965/com.yorkyu.fragmentlifedemo D/AFragment: onViewStateRestored: 05-24 17:24:18.622 22965-22965/com.yorkyu.fragmentlifedemo D/AFragment: onStart: 05-24 17:24:18.622 22965-22965/com.yorkyu.fragmentlifedemo D/DetailActivity: onStart: 05-24 17:24:18.623 22965-22965/com.yorkyu.fragmentlifedemo D/AFragment: onResume: 05-24 17:24:23.593 22965-22965/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onAttach: 05-24 17:24:23.594 22965-22965/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onCreate: 05-24 17:24:23.595 22965-22965/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onCreateView: 05-24 17:24:23.612 22965-22965/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onViewCreated: 05-24 17:24:23.612 22965-22965/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onActivityCreated: 05-24 17:24:23.613 22965-22965/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onViewStateRestored: 05-24 17:24:23.613 22965-22965/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onStart: 05-24 17:24:23.613 22965-22965/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onResume: 

这里按home键后

 05-24 17:26:50.408 22965-22965/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onPause: 05-24 17:26:50.409 22965-22965/com.yorkyu.fragmentlifedemo D/AFragment: onPause: 05-24 17:26:50.409 22965-22965/com.yorkyu.fragmentlifedemo D/DetailActivity: onPause: 05-24 17:26:50.726 22965-22965/com.yorkyu.fragmentlifedemo D/AFragment: onSaveInstanceState: 05-24 17:26:50.730 22965-22965/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onSaveInstanceState: 05-24 17:26:50.730 22965-22965/com.yorkyu.fragmentlifedemo D/DetailActivity: onSaveInstanceState: 05-24 17:26:50.730 22965-22965/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onStop: 05-24 17:26:50.730 22965-22965/com.yorkyu.fragmentlifedemo D/AFragment: onStop: 05-24 17:26:50.730 22965-22965/com.yorkyu.fragmentlifedemo D/DetailActivity: onStop:

也很好对应上面的图片了。相同之处是在onPause后调用了onSaveInstanceState。官方也介绍了如下,

不过,即使您什么都不做,也不实现 onSaveInstanceState(),Activity 类的 onSaveInstanceState() 默认实现也会恢复部分 Activity 状态。具体地讲,默认实现会为布局中的每个 View 调用相应的 onSaveInstanceState() 方法,让每个视图都能提供有关自身的应保存信息。Android 框架中几乎每个小部件都会根据需要实现此方法,以便在重建 Activity 时自动保存和恢复对 UI 所做的任何可见更改。例如,EditText 小部件保存用户输入的任何文本,CheckBox 小部件保存复选框的选中或未选中状态。您只需为想要保存其状态的每个小部件提供一个唯一的 ID(通过 android:id 属性)。如果小部件没有 ID,则系统无法保存其状态。

尽管 onSaveInstanceState() 的默认实现会保存有关您的Activity UI 的有用信息,您可能仍需替换它以保存更多信息。例如,您可能需要保存在 Activity 生命周期内发生了变化的成员值(它们可能与 UI 中恢复的值有关联,但默认情况下系统不会恢复储存这些 UI 值的成员)。

由于 onSaveInstanceState() 的默认实现有助于保存 UI 的状态,因此如果您为了保存更多状态信息而替换该方法,应始终先调用 onSaveInstanceState() 的超类实现,然后再执行任何操作。 同样,如果您替换 onRestoreInstanceState() 方法,也应调用它的超类实现,以便默认实现能够恢复视图状态。

注:由于无法保证系统会调用 onSaveInstanceState(),因此您只应利用它来记录 Activity 的瞬态(UI 的状态)— 切勿使用它来存储持久性数据,而应使用 onPause() 在用户离开 Activity 后存储持久性数据(例如应保存到数据库的数据)。

再看下按回的打印

05-24 17:31:42.592 22965-22965/com.yorkyu.fragmentlifedemo D/DetailActivity: onRestart: 05-24 17:31:42.593 22965-22965/com.yorkyu.fragmentlifedemo D/AFragment: onStart: 05-24 17:31:42.593 22965-22965/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onStart: 05-24 17:31:42.593 22965-22965/com.yorkyu.fragmentlifedemo D/DetailActivity: onStart: 05-24 17:31:42.594 22965-22965/com.yorkyu.fragmentlifedemo D/AFragment: onResume: 05-24 17:31:42.595 22965-22965/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onResume: 

执行的生命周期不多,不会走onCreate,而且都没有销毁,所以看不是问题。这里如果打开开发者选项中的不保留活动, 那么问题就来了,Activity会Destory

05-24 11:13:03.969 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onPause: 05-24 11:13:03.970 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onPause: 05-24 11:13:03.970 12289-12289/com.yorkyu.fragmentlifedemo D/DetailActivity: onPause: 05-24 11:13:04.911 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onSaveInstanceState: 05-24 11:13:04.912 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onSaveInstanceState: 05-24 11:13:04.928 12289-12289/com.yorkyu.fragmentlifedemo D/DetailActivity: onSaveInstanceState: 05-24 11:13:04.929 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onStop: 05-24 11:13:04.929 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onStop: 05-24 11:13:04.929 12289-12289/com.yorkyu.fragmentlifedemo D/DetailActivity: onStop: 05-24 11:13:04.950 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onDestroyView: 05-24 11:13:04.951 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onDestroyView: 05-24 11:13:04.952 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onDestroy: 05-24 11:13:04.952 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onDetach: 05-24 11:13:04.953 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onDestroy: 05-24 11:13:04.953 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onDetach: 05-24 11:13:04.953 12289-12289/com.yorkyu.fragmentlifedemo D/DetailActivity: onDestroy: 

返回打印结果

  05-24 11:13:31.331 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onAttach: 05-24 11:13:31.331 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onAttach: 05-24 11:13:31.332 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onCreate: 05-24 11:13:31.332 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onCreate: 05-24 11:13:31.358 12289-12289/com.yorkyu.fragmentlifedemo D/DetailActivity: onCreate: 05-24 11:13:31.360 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onCreateView: 05-24 11:13:31.392 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onViewCreated: 05-24 11:13:31.392 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onActivityCreated: 05-24 11:13:31.392 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onCreateView: 05-24 11:13:31.396 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onViewCreated: 05-24 11:13:31.396 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onActivityCreated: 05-24 11:13:31.396 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onViewStateRestored: 05-24 11:13:31.396 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onViewStateRestored: 05-24 11:13:31.396 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onAttach: 05-24 11:13:31.396 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onCreate: 05-24 11:13:31.397 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onDestroyView: 05-24 11:13:31.397 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onDestroyView: 05-24 11:13:31.397 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onCreateView: 05-24 11:13:31.400 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onViewCreated: 05-24 11:13:31.400 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onActivityCreated: 05-24 11:13:31.400 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onViewStateRestored: 05-24 11:13:31.400 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onStart: 05-24 11:13:31.400 12289-12289/com.yorkyu.fragmentlifedemo D/DetailActivity: onStart: 05-24 11:13:31.400 12289-12289/com.yorkyu.fragmentlifedemo D/DetailActivity: onRestoreInstanceState: 05-24 11:13:31.401 12289-12289/com.yorkyu.fragmentlifedemo D/AFragment: onResume: 05-24 11:13:31.405 12289-12289/com.yorkyu.fragmentlifedemo D/ActivityThreadInjector: clearCachedDrawables.05-24 11:13:36.367 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onAttach: 05-24 11:13:36.367 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onCreate: 05-24 11:13:36.399 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onAttach: 05-24 11:13:36.399 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onCreate: 05-24 11:13:36.400 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onCreateView: 05-24 11:13:36.413 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onViewCreated: 05-24 11:13:36.413 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onActivityCreated: 05-24 11:13:36.413 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onViewStateRestored: 05-24 11:13:36.414 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onStart: 05-24 11:13:36.414 12289-12289/com.yorkyu.fragmentlifedemo D/BFragment - main - 1: onResume: 

看到fragment B会先onCreate再destoryview,然后异步方法再new instance 一个新的, 因此产生多个实例,造成内存泄露。其实fragment A也同的情况。

解决办法

onSaveInstanceState里处理

必须在回调前通过fragmentManager移除当前页面的fragment,这样再走onCreate都会new Instance,这样也有一个问题就是每次跳转页面返回这个页面后一样会new Instance,性能不是太好。但解决了异步动态添加Fragment和Activity及Fragment自动恢复机制引起重复创建。我观察过这种情况大概在我这个应用里会造成每次1MB左右的内存泄露。

    @Override    public void onSaveInstanceState(Bundle outState) {        super.onSaveInstanceState(outState);        int backStackEntryCount = mChildFragmentManager.getBackStackEntryCount();        for (int i = 0; i < backStackEntryCount; i++) {            mChildFragmentManager.popBackStackImmediate();        }        Log.d(TAG, "onSaveInstanceState: ");    }

saveinstance

在OnCreate()或者onRestart()里判断

既然无法很好在onDestory去remove fragment,那么就配合activity 与fragment的自动保存机制,在onSaveInstanceState()里保留自动保留外的数据。大意代码如下:

    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.d(TAG, "onCreate: ");        if (savedInstanceState != null) {            // do something        } else {            // do anothering        }     }

参考

Fragment Transactions & Activity State Loss


原创粉丝点击