浅析Activity状态保存与恢复

本文详细解析了Android Activity的保存与恢复状态的过程,包括onSaveInstanceState和onRestoreInstanceState的调用时机和作用。文章指出,Activity在特定场景如关闭、后台运行、界面跳转和配置变更时会触发状态保存。在保存过程中,系统会保存View的状态,特别是设置了ID的View。同时,自定义View和Activity中的数据恢复策略也进行了说明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

接触Activity的时候有一个知识总是感觉用的地方不是特别多,但它也是Activity生命周期的一部分,那就是Activity的状态保存与恢复了,在生命周期中回调的方法是onSaveInstanceState和onRestoreInstanceState。那么什么时候我们需要用到它呢?
从字面意义上可判断场景如下:
1.关闭Activity(例如按下back键)
2.转向后台运行时(例如有电话打来、按下Home键等)
3.界面跳转
4.运行时配置发生变更(例如横竖屏切换)
... ... 


Activity的生命周期方法处理入口在ActivityThread中,查看如下源码:

ActivityThread{
private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) {
        r.state = new Bundle();
        r.state.setAllowFds(false);
        if (r.isPersistable()) {
            r.persistentState = new PersistableBundle();
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
                    r.persistentState);
        } else {
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
        }
    }
}


Instrumentation{
public void callActivityOnSaveInstanceState(Activity activity, Bundle outState) {
        activity.performSaveInstanceState(outState);
    }
}


Activity{
final void performSaveInstanceState(Bundle outState) {
        onSaveInstanceState(outState);
    }
protected void onSaveInstanceState(Bundle outState) {
        outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
    }
}


PhoneWindow{
public Bundle saveHierarchyState() {
mContentParent.saveHierarchyState(states);
}
}


View{
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
        if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
            mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
            Parcelable state = onSaveInstanceState();
            if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
                throw new IllegalStateException(
                        "Derived class did not call super.onSaveInstanceState()");
            }
            if (state != null) {
                // Log.i("View", "Freezing #" + Integer.toHexString(mID)
                // + ": " + state);
                container.put(mID, state);
            }
        }
    }
protected Parcelable onSaveInstanceState() {
        mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
        if (mStartActivityRequestWho != null) {
            BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE);
            state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
            return state;
        }
        return BaseSavedState.EMPTY_STATE;
    }
}


我们看到ActivityThread的callCallActivityOnSaveInstanceState方法会保存状态,往下看直到Activity的onSaveInstanceState,出现了我们熟悉的方法,这里源码中保存了PhoneWindow中所有View的状态,然后将WINDOW_HIERARCHY_TAG作为键保存到了bundle中。如果我们想在Activity中保存某些数据可以利用
outState存储。
mWindow.saveHierarchyState()最终会调用View的dispatchSaveInstanceState方法,如果设置了View的ID那么就会保存到SparseArray中,并且以mStartActivityRequestWho为标识符保存在BaseSavedState中,为了onRestoreInstanceState使用。


同理看下onRestoreInstanceState源码:


ActivityThread{
mInstrumentation.callActivityOnRestoreInstanceState
}


Instrumentation{
callActivityOnRestoreInstanceState{
activity.performRestoreInstanceState()
}
}


Activity{
final void performRestoreInstanceState(Bundle savedInstanceState) {
        onRestoreInstanceState(savedInstanceState);
        restoreManagedDialogs(savedInstanceState);
    }
protected void onRestoreInstanceState(Bundle savedInstanceState) {
if (mWindow != null) {
Bundle windowState = savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG);
if (windowState != null) {
mWindow.restoreHierarchyState(windowState);
}
}
}
}


PhoneWindow{
public void restoreHierarchyState(Bundle savedInstanceState) {
        SparseArray<Parcelable> savedStates  = savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
        if (savedStates != null) {
            mContentParent.restoreHierarchyState(savedStates);
        }
}
}
View{
public void restoreHierarchyState(SparseArray<Parcelable> container) {
        dispatchRestoreInstanceState(container);
    }
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
        if (mID != NO_ID) {
            Parcelable state = container.get(mID);
            if (state != null) {
                // Log.i("View", "Restoreing #" + Integer.toHexString(mID)
                // + ": " + state);
                mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
                onRestoreInstanceState(state);
            }
        }
    }
protected void onRestoreInstanceState(Parcelable state) {
        mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
        if (state != null && !(state instanceof AbsSavedState)) {
throw IllegalArgumentException(... ...)
        }
        if (state != null && state instanceof BaseSavedState) {
            mStartActivityRequestWho = ((BaseSavedState) state).mStartActivityRequestWhoSaved;
        }
    }
}


这些跟onSaveInstanceState方法相对应,注意一点是dispatchRestoreInstanceState方法中的state为空就不会调用onRestoreInstanceState,所以必须定义View一个ID才能恢复状态。
注意:
1.当我们自定义View的时候,如果有保存恢复状态的需求,重写这两个方法,并且super.onRestoreInstanceState(savedInstanceState);是一定要写的。
2.当我们在写Activity的时候,由于系统是自动保存视图层次结构的状态信息,也就说EditText等控件状态是默认保存的,只要我们给它设置一个ID,横竖屏切换状态也会保留之前的数据。
3.恢复数据:我们可以通过onCreate的参数bundle是否为空来判断是否保存了数据信息;或者通过回调方法onRestoreInstanceState恢复。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值