首先看下段状态保存方法示例代码
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
}
(1) onCreate(Bundle savedInstanceState) 方法
Activity 创建时回调 : 该方法会自动传入一个 Bundle 对象, 该 Bundle 对象就是上次被系统销毁时在 onSaveInstanceState 或者 onRestoreInstanceState 中保存的数据;
– 注意 : 只有是系统自动回收的时候才会保存 Bundle 对象数据;
– Bundle 对象来源 : onCreate() 方法中的 Bundle 对象参数, 是在 onSaveInstance() 或者 onRestoreInstanceState() 方法中保存的 Bundle 对象;
.
(2) onSaveInstanceState(Bundle outState) 方法
outState 参数作用 :
用来保存实例状态的,这个“实例”不是指的 Activity 对象,而是它所在的进程,因为Activity 的销毁是因为它所在的进程被杀掉而造成的。onSaveInstanceState()是在系统感觉需要销毁Activity时调用的。。
– 数据保存 : Activity 声明周期结束的时候, 需要保存 Activity 状态的时候, 会将要保存的数据使用键值对的形式 保存在 Bundle 对象中;
– 恢复数据 : 在 Activity 的 onCreate()方法 创建 Activity 的时候会传入一个 Bundle 对象, 这个 Bundle 对象就是这个 outState 参数;
调用时机 : Activity 容易被销毁的时候调用, 注意是容易被销毁, 也可能没有销毁就调用了;
– 按下Home键 : Activity 进入了后台, 此时会调用该方法;
– 按下电源键 : 屏幕关闭, Activity 进入后台;
– 启动其它 Activity : Activity 被压入了任务栈的栈底;
– 横竖屏切换 : 会销毁当前 Activity 并重新创建;
onSaveInstanceState方法调用注意事项 :
– 用户主动销毁不会调用 : 当用户点击回退键 或者 调用了 finish() 方法, 不会调用该方法;
– 调用时机不固定 : 该方法一定是在 onStop() 方法之前调用, 但是不确定是在 onPause() 方法之前 还是 之后调用;
– 布局中组件状态存储 : 每个组件都 实现了 onSaveInstance() 方法, 在调用函数的时候, 会自动保存组件的状态, 注意, 只有有 id 的组件才会保存;
– 关于默认的 super.onSaveInstanceState(outState) : 该默认的方法是实现 组件状态保存的;
Activity 类已实现了onSaveInstanceState(),在 onSaveInstanceState() 的默认实现中,会调用所有控件的相关方法,把控件们的状态都保存下来,比如 EditText 中输入的文字、CheckBox 是否被选中等等。然而不是所有的控件都能被保存,这取决于你是否在 layout 文件中为控件赋了一个名字(android:id)。有名的就存,无名的不管。
(3) onRestoreInstanceState(Bundle savedInstanceState) 方法
方法回调时机 : 在 Activity 被系统销毁之后 恢复 Activity 时被调用, 只有销毁了之后重建的时候才调用, 如果内存充足, 系统没有销毁这个 Activity, 就不需要调用;
– Bundle 对象传递 : 该方法保存的 Bundle 对象在 Activity 恢复的时候也会通过参数传递到 onCreate() 方法中;
– 位于生命周期位置吧 : 该方法在 onResume() 方法之前保存信息;
二 代码示例
Activity 主要代码 :
package com.example.octopus_saveinstance;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState != null)
System.out.println("onCreate() : " + savedInstanceState.getString("octopus"));
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// 重新创建后恢复缓存的数据
System.out.println("onRestoreInstanceState() : " + savedInstanceState.getString("octopus"));
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// 被销毁前缓存一些数据
outState.putString("octopus", "www.octopus.org.cn");
System.out.println("onSaveInstanceState() : save date www.octopus.org.cn");
}
}
运行结果 : 在运行的过程中 旋转屏幕;
I/System.out( 8167): onSaveInstanceState() : save date www.octopus.org.cn
I/System.out( 8167): onCreate() : www.octopus.org.cn
I/System.out( 8167): onRestoreInstanceState() : www.octopus.org.cn
-
只要Activity不finish,Activity进入后台(比如Home键,跳转到其他的Activity),则其就会调用onSaveInstanceState(Bundle outState)方法,而且这个方法是在onPause方法之间进行调用的。
-
如果Activity是执行了finish方法,才进入的后台,则不调用这个onSaveInstanceState(Bundle outState),而且下次再进入时,也不会使用这个保存的数据。
-
在系统杀掉Activity所在的进程时,onSaveInstanceState(Bundle outState)方法根本就没有调用过。
总结:系统在杀进程时,不可能有时间去执行多余的代码,也只有这种方法,才能保存Activity里的最新数据,所以onSaveInstanceState(Bundle outState)会被执行多次,并不是只有一次。
Fragment的onCreate()中调用setRetainInstance(true)和不调用对比
不调用:
onAttach—onCreate–onCreateView–onActivityCreated–onStart–onResume正常的生命周期
切换屏幕:
onPause–onStop–onDestroyView–onDestroy–onDetach – 每次都销毁Fragment
onAttach—onCreate–onCreateView–onActivityCreated–onStart–onResume
每次都会走onCreate,每次创建一个新的Fragment
调用:
onAttach—onCreate–onCreateView–onActivityCreated–onStart–onResume 正常的生命周期
切换屏幕:
onPause–onStop–onDestroyView–onDetach – 不销毁Fragment
onAttach–onCreateView–onActivityCreated–onStart–onResume //不执行onCreate
有Fragment实例之后,再切换,不会走onDestroy,不会走onCreate,并且发现fragment实例是之前同一个
问题:TransactionTooLargeException activityStopped
1.有可能 FragmentStatePagerAdapter的实现有缺陷,因为其默认实现会持续保存历史Fragment实例的状态数据历史,在逐渐地积累、保存数据后,最终导致发送的数据包体积超过限制200KB 。
解决方案:重写FragmentStatePagerAdapter的saveState方法,使其不保存历史Fragment实例的状态数据。
2.如果不是上叙原因 那么 继续分析该问题,深入代码进行白盒分析,根据日志输出和代码结构,最后定位问题原因是构建Fragment实例时传递ArrayList给Fragment的构造函数,
在Fragment的构造函数内部将该ArrayList作为Parcelable存放在Bundle,以供该Fragment初始化时从bundle中读取, 在数据量比较大时,就会抛出TransactionTooLargeException。
解决方案:构建Fragment实例时传递ArrayList给Fragment的构造函数,在Fragment被加载时无需从Bundle中读取,这样可避免TransactionTooLargeException,又提高程序执行效率。
3.Activity 的onSaveInstanceState上保存数据量过大一般由于页面页面子布局太多 当Activity离开时onSaveInstanceState(Bundle超过Binder限制)
结局方案:重写onSaveInstanceState()禁用父类调用,实现自己的状态保存或忽略。