演练 - 保存活动状态

我们已在活动生命周期指南中介绍了保存状态背后的理论:现在,让我们演练一个示例。

活动状态演练

让我们打开 ActivityLifecycle_Start 项目(在 ActivityLifecycle 示例中),生成它,然后运行它。 这是一个非常简单的项目,通过两个活动来演示活动生命周期以及如何调用各种生命周期方法。 启动应用程序时,将显示屏幕 MainActivity

Activity A screen

查看状态转换

此示例中的每个方法都会写入 IDE 应用程序输出窗口以指示活动状态。 (要在 Visual Studio 中打开输出窗口,请键入 Ctrl-Alt-O;要在 Visual Studio for Mac 中打开输出窗口,请单击“查看”>“填充”>“应用程序输出”。)

应用首次启动时,输出窗口将显示活动 A 的状态更改:

[ActivityLifecycle.MainActivity] Activity A - OnCreate
[ActivityLifecycle.MainActivity] Activity A - OnStart
[ActivityLifecycle.MainActivity] Activity A - OnResume

单击“启动活动 B”按钮时,可以看到活动 A 会在活动 B 经历其状态更改时暂停和停止:

[ActivityLifecycle.MainActivity] Activity A - OnPause
[ActivityLifecycle.SecondActivity] Activity B - OnCreate
[ActivityLifecycle.SecondActivity] Activity B - OnStart
[ActivityLifecycle.SecondActivity] Activity B - OnResume
[ActivityLifecycle.MainActivity] Activity A - OnStop

因此,活动 B 会代替活动 A 启动并显示:

Activity B screen

单击“后退”按钮时,活动 B 将被销毁并恢复活动 A:

[ActivityLifecycle.SecondActivity] Activity B - OnPause
[ActivityLifecycle.MainActivity] Activity A - OnRestart
[ActivityLifecycle.MainActivity] Activity A - OnStart
[ActivityLifecycle.MainActivity] Activity A - OnResume
[ActivityLifecycle.SecondActivity] Activity B - OnStop
[ActivityLifecycle.SecondActivity] Activity B - OnDestroy

添加单击计数器

接下来,我们将更改应用程序,以便设置一个在单击它时进行计数并显示单击次数的按钮。 首先,我们将 _counter 实例变量添加到 MainActivity

int _counter = 0;

接下来,我们编辑 Resource/layout/Main.axml 布局文件,并添加一个新的 clickButton,以显示用户单击该按钮的次数。 生成的 Main.axml 的外观应如下所示:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <Button
        android:id="@+id/myButton"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/mybutton_text" />
    <Button
        android:id="@+id/clickButton"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/counterbutton_text" />
</LinearLayout>

我们将以下代码添加到 MainActivityOnCreate 方法的末尾 - 此代码可处理来自 clickButton 的单击事件:

var clickbutton = FindViewById<Button> (Resource.Id.clickButton);
clickbutton.Text = Resources.GetString (
    Resource.String.counterbutton_text, _counter);
clickbutton.Click += (object sender, System.EventArgs e) =>
{
    _counter++;
    clickbutton.Text = Resources.GetString (
        Resource.String.counterbutton_text, _counter);
} ;

再次生成并运行应用时,将显示一个新按钮,该按钮会在每次单击时递增并显示 _counter 的值:

Add touch count

但当我们将设备旋转到横向模式时,将会丢失此计数:

Rotating to landscape sets the count back to zero

通过检查应用程序输出,我们会看到活动 A 在从纵向到横向模式的旋转过程中依次经历暂停、停止、销毁、重新创建、重启和恢复:

[ActivityLifecycle.MainActivity] Activity A - OnPause
[ActivityLifecycle.MainActivity] Activity A - OnStop
[ActivityLifecycle.MainActivity] Activity A - On Destroy

[ActivityLifecycle.MainActivity] Activity A - OnCreate
[ActivityLifecycle.MainActivity] Activity A - OnStart
[ActivityLifecycle.MainActivity] Activity A - OnResume

由于活动 A 在旋转设备时被销毁并重新创建,因此其实例状态会丢失。 接下来,我们将添加代码以保存和还原实例状态。

添加代码以保留实例状态

我们将向 MainActivity 添加一个方法来保存实例状态。 在销毁活动 A 之前,Android 会自动调用 OnSaveInstanceState,并传入可用于存储实例状态的捆绑包。 我们使用它将单击计数保存为整数值:

protected override void OnSaveInstanceState (Bundle outState)
{
    outState.PutInt ("click_count", _counter);
    Log.Debug(GetType().FullName, "Activity A - Saving instance state");

    // always call the base implementation!
    base.OnSaveInstanceState (outState);    
}

在重新创建和恢复活动 A 时,Android 会将此 Bundle 传回 OnCreate 方法。 让我们向 OnCreate 添加代码以从传入的 Bundle 还原 _counter 值。 在定义 clickbutton 行之前添加以下代码:

if (bundle != null)
{
    _counter = bundle.GetInt ("click_count", 0);
    Log.Debug(GetType().FullName, "Activity A - Recovered instance state");
}

再次生成并运行应用,然后单击第二个按钮几次。 将设备旋转到横向模式时,将会保留计数!

Rotating the screen shows count of four preserved

我们观察一下输出窗口,看看发生了什么:

[ActivityLifecycle.MainActivity] Activity A - OnPause
[ActivityLifecycle.MainActivity] Activity A - Saving instance state
[ActivityLifecycle.MainActivity] Activity A - OnStop
[ActivityLifecycle.MainActivity] Activity A - On Destroy

[ActivityLifecycle.MainActivity] Activity A - OnCreate
[ActivityLifecycle.MainActivity] Activity A - Recovered instance state
[ActivityLifecycle.MainActivity] Activity A - OnStart
[ActivityLifecycle.MainActivity] Activity A - OnResume

在调用 OnStop 方法之前,调用了新的 OnSaveInstanceState 方法以将 _counter 值保存在 Bundle 中。 Android 在调用 OnCreate 方法时已将其此 Bundle 传递回我们,并且我们能够使用它将 _counter 值还原为我们离开时的位置。

总结

在此演练中,我们已使用活动生命周期的知识来保留状态数据。