我们在开发时需要保存Activity和Fragment的状态,以防他们被销毁。当我们的Activity不可见时,系统为了恢复内存而销毁某个Activity时,之后用户返回到这个Activity时,系统会重建这个Activity。这时如果不将状态保存下来,用户体验会非常差。Android解决这个问题,给开发者提供了一个回调方法onSaveInstanceState()来确保Activity状态的重要信息得到保留。系统会先调用onSaveInstanceState方法,然后再使Activity变得易于销毁。系统会向该方法传递一个Bundle,我们可以借用这个Bundle来保存重要信息。然后系统会终止此Activity,之后用户返回此Activity,系统会重建Activity,并将Bundle传递给onCreate方法和onRestoreInstanceState方法,我们可通过这两个方法来恢复状态。还有当我们Android手机配置发生变更时,如从原先的竖屏变为横屏,系统语言从中文变为英文等等。当发生这些变化时,Android为了让应用加载匹配的资源,默认会销毁旧Activity新建一个新的Activity(无论什么启动模式都是销毁后重建)。
1:Activity的状态保存
Activity保存和恢复状态涉及到三个方法:
onCreate(): 接收onSaveInstanceState方法传过来的Bundle。
onSaveInstanceState():将要保存的数据保存在Bundle中并传给onCreate和onRestoreInstanceState方法。
onRestoreInstanceState():接收onSaveInstanceState方法传过来的Bundle。
public class SaveActivity extends AppCompatActivity {
private static final String TAG = "SaveActivity--lsc";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_save);
//一般都在onCreate方法中恢复数据,
if (savedInstanceState != null){
//2:接收数据并恢复
savedInstanceState.getString("saveString");
}
Log.d(TAG, "onCreate: Activity创建了");
}
/** 在onStop方法之前调用,
* 无法保证系统会在销毁 Activity 前调用 onSaveInstanceState(),
* 因为存在不需要保存状态的情况(例如用户使用“返回”按钮离开 Activity 时,
* 因为用户的行为是在显式关闭 Activity)
*/
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
//1:保存数据到Bundle中
outState.putString("saveString","我是保存的字符串");
Log.d(TAG, "onSaveInstanceState: ");
}
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.d(TAG, "onRestoreInstanceState: ");
}
上述代码即可保存Activity状态。不过,即使我们什么都不做,不实现 onSaveInstanceState(),Activity 的 onSaveInstanceState() 默认实现也会恢复部分Activity状态。具体地讲,默认实现会为布局中的每个 View 调用相应的 onSaveInstanceState()方法,让每个视图都能提供有关自身的应保存信息。Android框架中几乎每个小部件都会根据需要实现此方法,以便在重建 Activity时自动保存和恢复对UI所做的任何可见更改。例如,EditText 小部件保存用户输入的任何文本,CheckBox小部件保存复选框的选中或未选中状态。我们只需为想要保存其状态的每个小部件提供一个唯一的ID(通过 android:id 属性)。如果小部件没有 ID,则系统无法保存其状态。
1.1:配置发生变更
这里要重点说明一下配置发生变更的情况,这个比较复杂一点。
1.1.1:系统默认行为
当配置发生变更时,Android会销毁当前Activity并立即重建当前Activity(此时这两个Activity对象不一样)。
1.2.2:限定屏幕方向
如果我们的应用只支持竖屏的话我们可以给Activity配置如下标签:
<activity android:name=".MainActivity"
android:screenOrientation="portrait"/>
<!-- portrait表示竖屏,landscap表示横屏 -->
此时屏幕方向发生变化,Android不会销毁并重建这个Activity。
1.2.3:自己处理变更
给Activity设置下列标签,屏幕方向变化的时候,activity不会销毁重建,但会变横屏。
<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize|keyboardHidder"/>
<!-- 屏幕方向变化,宽高变化,键盘可见性。 -->
此时Activity会回调onConfigurationChanged方法,我们可以在这里处理这些变更。但一般情况下我们还是让系统处理这些变更,自己处理会让逻辑变得复杂。
我们自定义控件时,若是也想保存视图状态需要重写onSaveInstanceState和onRestoreInstanceState方法。
用户按home键时回调onSaveInstanceState()
用户来电话时回调onSaveInstanceState()
用户按返回键时不会回调onSaveInstanceState()
2:保存Fragment状态
当Activity销毁和新建时,必然会关联到依附于Activity的Fragment的销毁和创建。若是不做处理Fragment也会随着Activity销毁和新建。我们实际应用中有两种方式进行保存:1.保存Fragment对象。2.保存数据创建新的Fragment对象。
2.1:保存Fragment对象
public class SaveActivity extends AppCompatActivity {
private static final String TAG = "SaveActivity--lsc";
private Fragment mSaveFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_save);
FragmentManager fragmentManager = getSupportFragmentManager();
//根据tag获取fragment对象。
mSaveFragment = fragmentManager.findFragmentByTag("SaveFragment");
if (mSaveFragment == null) {
mSaveFragment = new SaveFragment();
//给Fragment对象添加tag “SaveFragment”
fragmentManager.beginTransaction().replace(R.id.fragment_layout, mSaveFragment, "SaveFragment").commit();
}
Log.d(TAG, "onCreate: Activity创建了");
}
}
2.2:保存数据创建新的fragment
保存数据创建新的fragment这种方式涉及到三个方法:
onSaveInstanceState():保存数据到Bundle中。
onActivityCreated(): 读取Bundle。
onCreate(): 读取Bundle。
public class SaveFragment extends Fragment {
private static final String TAG = "SaveFragment--lsc";
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate: ");
if (savedInstanceState != null){
}
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Log.d(TAG, "onActivityCreated: ");
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
Log.d(TAG, "onSaveInstanceState: ");
}
}
若是不涉及Fragment更改加载资源,推荐使用保存Fragment对象的方式。