Android中的状态保存

在Android中,当我们的Acitivity 被系统干掉的时候,会给我们一次保存数据的机会,我们通过可以通过这个机会来将需要的数据保存下来,然后在用户从新打开APP的时候,把这个数据恢复。

先来看一下Activity是怎么保存数据的

先来看一下onSaveInstanceState()方法

protected void onSaveInstanceState(Bundle outState) {
    outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
    Parcelable p = mFragments.saveAllState();
    if (p != null) {
        outState.putParcelable(FRAGMENTS_TAG, p);
    }
    getApplication().dispatchActivitySaveInstanceState(this, outState);
}

代码非常的简单,保存了3个对象的状态,分别是mWindow, mFragments和 Application 对象
mWindow的实际实现对象是PhoneWindow, 我们进入到 saveHierarchyState() 方法看看

    /** {@inheritDoc} */
@Override
public Bundle saveHierarchyState() {
    Bundle outState = new Bundle();
    if (mContentParent == null) {
        return outState;
    }

    SparseArray<Parcelable> states = new SparseArray<Parcelable>();
    mContentParent.saveHierarchyState(states);
    outState.putSparseParcelableArray(VIEWS_TAG, states);

    // Save the focused view ID.
    final View focusedView = mContentParent.findFocus();
    if (focusedView != null && focusedView.getId() != View.NO_ID) {
        outState.putInt(FOCUSED_ID_TAG, focusedView.getId());
    }

    // save the panels
    SparseArray<Parcelable> panelStates = new SparseArray<Parcelable>();
    savePanelState(panelStates);
    if (panelStates.size() > 0) {
        outState.putSparseParcelableArray(PANELS_TAG, panelStates);
    }

    if (mDecorContentParent != null) {
        SparseArray<Parcelable> actionBarStates = new SparseArray<Parcelable>();
        mDecorContentParent.saveToolbarHierarchyState(actionBarStates);
        outState.putSparseParcelableArray(ACTION_BAR_TAG, actionBarStates);
    }
    return outState;
}

首先是尝试去保存 mContentParent 的状态, 这个mContentParent 实际上就是在DecorView中 id 为 content的 ViewGroup, 我们setContetnView中添加的View就是放在了它的下面。mContentParent 是FrameLayout, 最终类型就是ViewGroup。先看看ViewGroup的父类 View怎么实现 saveHierarchyState .saveHierarchyState 调用了 dispatchSaveInstanceState,而这个方法是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);
        }
    }
}

实际上就是把onSaveInstanceState 放的返回值 保存了起来,在这里我们可以看到,这里是根据mID 也就是我们在布局文件为View指定的id来保存状态的,同时因为 SparseArray 是一个key ,value的键值对集合,所以这里,如果指定了相同的key,前面的key对应的view状态会被后来的覆盖掉,所以要尽量避免key的重复。

再来看 ViewGroup的 dispatchSaveInstanceState 方法。

@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
    super.dispatchSaveInstanceState(container);
    final int count = mChildrenCount;
    final View[] children = mChildren;
    for (int i = 0; i < count; i++) {
        View c = children[i];
        if ((c.mViewFlags & PARENT_SAVE_DISABLED_MASK) != PARENT_SAVE_DISABLED) {
            c.dispatchSaveInstanceState(container);
        }
    }
}

ViewGroup 首先调用了View的dispatchSaveInstanceState 方法来保存自身的状态,然后调用了其子View 的dispatchSaveInstanceState 方法来保存 子view的 状态。这样最终的ViewTree的状态就被保存下来了。

Activity 把View的状态都存下来了之后,再开始存Fragment的状态
Fragment状态的保存实在FragmentManager的saveAllState()方法来完成的,在这个方法当中调用了
saveFragmentBasicState()方法,

 Bundle saveFragmentBasicState(Fragment f) {
    Bundle result = null;

    if (mStateBundle == null) {
        mStateBundle = new Bundle();
    }
    f.performSaveInstanceState(mStateBundle);
    if (!mStateBundle.isEmpty()) {
        result = mStateBundle;
        mStateBundle = null;
    }

    if (f.mView != null) {
        saveFragmentViewState(f);
    }
    if (f.mSavedViewState != null) {
        if (result == null) {
            result = new Bundle();
        }
        result.putSparseParcelableArray(
                FragmentManagerImpl.VIEW_STATE_TAG, f.mSavedViewState);
    }
    if (!f.mUserVisibleHint) {
        if (result == null) {
            result = new Bundle();
        }
        // Only add this if it's not the default value
        result.putBoolean(FragmentManagerImpl.USER_VISIBLE_HINT_TAG, f.mUserVisibleHint);
    }

    return result;
}

performSaveInstanceState 的代码如下

void performSaveInstanceState(Bundle outState) {
    onSaveInstanceState(outState);
    if (mChildFragmentManager != null) {
        Parcelable p = mChildFragmentManager.saveAllState();
        if (p != null) {
            outState.putParcelable(Activity.FRAGMENTS_TAG, p);
        }
    }
}

performSaveInstanceState onSaveInstanceState 方法,在这个方法里面我们可以保存自己想要保存的信息,之后又开始调用FragmentManamager 的 saveAllState()方法,就是递归调用了。跳出这个方法,接着看 saveFragmentViewState()

    void saveFragmentViewState(Fragment f) {
    if (f.mView == null) {
        return;
    }
    if (mStateArray == null) {
        mStateArray = new SparseArray<Parcelable>();
    } else {
        mStateArray.clear();
    }
    f.mView.saveHierarchyState(mStateArray);
    if (mStateArray.size() > 0) {
        f.mSavedViewState = mStateArray;
        mStateArray = null;
    }
}

关键的代码是 f.mView.saveHierarchyState(mStateArray);这个方法保存开始保存View的信息, 这个就是在开始分析的保存ViewTree那一部分的内容了。

在上面的内容保存完了还要保存当前已经添加的Fragment的信息,和回退栈的信息。

// Build list of currently added fragments.
   if (mAdded != null) {
       N = mAdded.size();
       if (N > 0) {
           added = new int[N];
           for (int i=0; i<N; i++) {
               added[i] = mAdded.get(i).mIndex;
               if (added[i] < 0) {
                   throwException(new IllegalStateException(
                           "Failure saving state: active " + mAdded.get(i)
                           + " has cleared index: " + added[i]));
               }
               if (DEBUG) Log.v(TAG, "saveAllState: adding fragment #" + i
                       + ": " + mAdded.get(i));
           }
       }
   }

   // Now save back stack.
   if (mBackStack != null) {
       N = mBackStack.size();
       if (N > 0) {
           backStack = new BackStackState[N];
           for (int i=0; i<N; i++) {
               backStack[i] = new BackStackState(this, mBackStack.get(i));
               if (DEBUG) Log.v(TAG, "saveAllState: adding back stack #" + i
                       + ": " + mBackStack.get(i));
           }
       }
   }

所以在Fragment保存的内容当中一共是当前active fragment的信息,已经添加的fragment 信息,和回退栈的信息,三部分

在回到Activity 的onSaveInstanceState 方法当中去,最后一步就是通知注册在Application当中的Activity 生命周期的回调。

onSaveInstanceState() 一般在onStop之前调用,再到ActivityThread的performStopActivity方法看看

final void performStopActivity(IBinder token, boolean saveState, String reason) {
    ActivityClientRecord r = mActivities.get(token);
    performStopActivityInner(r, null, false, saveState, reason);
}

首先获取了了 一个 ActivityClientRecord 对象,然后在 performStopActivityInner 方法当中,根据saveState的值,会去调用 callCallActivityOnSaveInstanceState 方法

代码如下

 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);
    }
}

接着看 callActivityOnSaveInstanceState 方法,里面直接 调用了 activity.performSaveInstanceState(outState), 再回到Activity看performSaveInstanceState,

final void performSaveInstanceState(Bundle outState) {
    onSaveInstanceState(outState);
    saveManagedDialogs(outState);
    mActivityTransitionState.saveState(outState);
    storeHasCurrentPermissionRequest(outState);
    if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
}

调用了 onSaveInstanceState 这个就是前文分析的入口了,除了前文分析的保存的状态还保存了,dialog ,activity transaction, 权限相关的信息 ,这些信息都被存入了outState当中。而这个正是ActivityClientRecrod的成员变量之一,ActivityClientRecrod有最终被存在了 ActivityThread 的 mActivities 集合当中。

这里需要注意

现在保存的信息都分析完了,后面用户再回来的时候就需要将保存的信息再传给Activity,启动Acitivty在ActivityThread中的 performLaunchActivity 方法,
在这个方法当有如下代码

if (r.isPersistable()) {
    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
    mInstrumentation.callActivityOnCreate(activity, r.state);
}

这里最终将ActivityClientRecord对象中保存的 state 在传递给 activity的 onCreate 方法,这样就可以在onCreate的时候实现对上一个Activity对象状态的恢复。