• 【Android】11.2 通过重写对应的方法保存和恢复实例的状态


    分类:C#、Android、VS2015;

    创建日期:2016-02-21

    一、简介

    通过重写(也叫回调)对应的方法来管理Activity的生命周期,比如用户旋转屏幕时应用程序要能自动保存和恢复实例的状态,这对于开发一个健壮而又灵活的应用程序而言至关重要。

    1、本节要点

    一旦真正理解了Activity的生命周期,就可以轻松自如地通过C#代码去控制它了。这一节我们主要学习如何用Boundle存储简单类型的数据(比如int、double、string、bool、……等)。

    当一个Activity停止或销毁时,系统会存储该Activity的状态,这个保存的状态称为实例状态。在Activity的生命周期中,Android提供了存储实例状态的三个选项:

    • 在叫做Boundle的“key/value”字典中保存元数据的值。
    • 创建一个自定义的类,用该类来维持复杂数据的状态,比如图像。
    • 不保存Activity的任何状态。即:以上两个事都不做。比如,从活动A转到活动B后,活动A原来的临时数据丢就丢了,反正活动B也用不到这些数据。

    下面主要介绍前两个选项。

    2、利用Bundle保存和恢复场景(实例)的状态

    由于实例状态保存在称为Boundle的“key/value”字典中,因此,当创建一个Activty时,就可以在重写的OnCreate()方法中传递boundle来恢复原来的实例状态。

    但是,不建议将图像等复杂数据保存在boundle中,这是因为图像不容易被串行化为“key/value”,这种情况下,应该将整个图像文件作为一个简单数据类型来看待(仅仅用字符串保存图像的文件名或URL,而不是保存完整的图像数据)。

    Bundle为Activity提供了“保存”和“恢复”实例状态的两个方法:

    • OnSaveInstanceState – 当开始销毁Activity时Android将自动调用此方法。在这个方法中,可通过一系列【“键/值”对】来存储需要保存的项。
    • OnRestoreInstanceState – 当OnCreate()方法完成后Android将自动调用此方法,通过重写该方法,就可以恢复原来的状态。

    注:不要对“状态”这个词迷惑了,它的本质含义举例来说就是:当活动A从可见变为不可见,然后再从不可见转为可见后,活动A原来的数据、选项、……等“状态”还需要在再次可见时重现吗,如果需要重现,就先在重写的OnSaveInstanceState()方法中保存这些数据、选项、……等场景,然后再在重写的OnRestoreInstanceState()方法中恢复这些场景。这样一来,不管Android系统内部怎样折腾,这些数据也不会丢了,仍然能在重新可见时重现原来的场景。

    下图演示了如何使用这两个方法:

    image

    (1)OnSaveInstanceState()方法

    当一个Activity被Paused或者Stopped时,如果这个Activity对象仍然被保留在内存中,那么就可以简单地通过Resume让这个Activity返回到前台。可是,当内存紧张时,系统可能会销毁这个Activity,这样程序就无法简单地通过Resume还原这个Activity了。如果用户还想返回到这个Activity,系统必须新建一个Activity让用户觉得原来的那个Activity还存在,可是如何还原到销毁前的状态呢?解决办法就是让系统在杀死那个Activity之前先调用OnSaveInstanceState()来保存原来的状态,这样就可以用它去恢复了。

    具体办法是:在重写的OnCreate()方法或者重写的OnRestoreInstanceState()方法中从传递的Bundle中解析出保存的信息,并用它来恢复Activity原来的状态。

    如果原来并没有储存状态信息,此时传入的Bundle将为null,比如第1次创建Activity时就是这种情况。

    总之,将Activity状态完整地返回给用户的两种情况有:

    (a)如果原来的Activity对象还保存在内存中,系统就直接通过Resumed还原原来的状态;

    (b)如果原来的Activity对象已经不存在,系统就新建一个Activity,并检查是否保存了原来的状态,如果保存了就还原它,否则将新建的状态设置为null。

    注意:系统不一定每次都在销毁这个Activity之前调用OnSaveInstanceState()方法 ,比如用户使用【Back】键关闭了这个Activity。在这种情况下,系统通常会在OnStop()方法之前或者在OnPause()之前调用OnSaveInstanceState(),而不是在销毁前去调用它。

    还有一些情况,比如在layout文件夹下的布局文件中声明的控件,即使你不显式调用OnSaveInstanceState()方法,系统仍然会默认将其状态保存下来。但前提是你必须为想要保存其状态的控件(widget)提供一个唯一的ID。如果不给widget指定ID,系统是无法保存它的状态的。

    另外,通过把android:saveEnabled 设置为"false",或者调用 SetSaveEnabled() 方法,也可以显式地阻止layout中的某个view保存状态。不过,通常不应该禁用保存,但是,假如你需要恢复activity UI的各个不同的状态,也许可以这么做。

    尽管默认实现的 OnSaveInstanceState() 方法会保存activity UI的有用信息,但你仍然需要重写(override)它来存入更多的信息。 例如,你可能需要保存在activity生命周期中改变的成员变量值(比如恢复UI的值,默认情况下,这些UI状态的成员变量值是不会被恢复的)。

    因为默认实现的 OnSaveInstanceState() 方法已经帮你保存了一些UI的状态,所以如果你为了保存更多的状态信息而重写此方法,那么在执行自己的代码之前应该确保先调用一次父类的 OnSaveInstanceState() 方法。同理,如果重写 OnRestoreInstanceState(),也应该调用一次父类的该方法,这样缺省的代码就能正常恢复view的状态了。

    注意:因为 OnSaveInstanceState() 并不保证每次都会被调用,所以你应该只用它来记录activity的一些临时状态信息(UI的状态)——千万不要用它来保存那些需要长久保存的数据(比如那些需要存入数据库里的数据),而是应该在用户离开activity的时候在重写的 OnPause()方法中来保存这些永久性数据。

    一个检测应用程序状态恢复能力的好办法就是旋转设备,使屏幕方向发生改变(在模拟器中可通过按<Ctrl>+<F11>让其旋转)。当屏幕的方向改变时,因为要换用符合实际屏幕参数的资源,所以系统会销毁并重建这个activity。正因如此,你的activity在被重建时能完整地恢复状态非常重要,因为用户可能会在使用应用程序时频繁地旋转屏幕。

    本节的例子演示了如何在重写的OnSaveInstanceState()方法中保存状态数据。在这个例子中,用户每单击一次按钮,都会将count的值加1,并在TextView中将count的值显示出来。当改变配置状态时--比如用户旋转屏幕时--会丢失count的值(此时boundle将会为null),重而写该Activity的OnSaveInstanceState()方法即可保存count的值。这样一来,当设备旋转到一个新的状态时(比如按<Ctrl>+<F11>将模拟器从纵向变为横向),由于count的值已经保存到boundle中,因此可通过下面的办法重新获取这个值:

    count = bundle.GetInt ("counter", -1);

    注意:调用base.OnSaveInstanceState (outState)非常重要,这能确保始终存储视图的层次结构的所有原始状态。

    (2)OnRestoreInstanceState()方法

    设备的某些配置可能会在运行时发生变化(比如屏幕方向、键盘可用性以及国家语言等状态发生了变化)。当发生这些变化时,Android会重建这个运行中的activity,即:系统会先调用OnDestroy() ,再调用OnCreate()。这种设计有助于应用程序适用新的参数配置,措施就是通过把你预置的可替换资源(比如对应各种屏幕方向和尺寸的layout)自动重新装载到应用程序中。

    如果你采取了适当的设计,让activity能够正确地处理这些因为屏幕方向而引起的重启,并能如上面所述的那样恢复activity的状态,那么你的应用程序将对生命周期中其它的意外事件更具适应能力。

    处理这类重启的最佳方式,就是在重写的OnSaveInstanceState()方法中保存状态,在重写的OnRestoreInstanceState()或者OnCreate()中恢复状态。

    这个例子是通过在重写的OnCreate()方法恢复状态的。

    实际上,在许多情况下,并不需要总是重写OnRestoreInstanceState()方法,因为大多数活动都可以在重写的OnCreate()方法中直接恢复原来的状态。

    二、示例2—状态保存和恢复

    此示例演示了如何保存和恢复用户旋转屏幕前后的状态。

    1、运行截图

    单击按钮3次的截图如下:

    image

    按【Ctrl】+【F11】后的截图如下:

    image

    再次按【Ctrl】+【F11】再次切换为纵向放置。

    2、实现步骤

    (1)添加ch1102_Layout.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">
        <TextView
            android:text="你单击了按钮 0 次"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/textViewCount"
            android:layout_margin="20dp"
            android:gravity="center_horizontal" />
        <Button
            android:id="@+id/btn1"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="请多次单击" />
        <TextView
            android:text="提示:【Ctrl】+【F11】用于控制模拟器的屏幕【纵向/横向】转换,多次按它观察屏幕旋转后单击按钮的次数显示的情况。"
            android:textAppearance="?android:attr/textAppearanceSmall"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/textView2"
            android:layout_margin="20dp" />
    </LinearLayout>

    (2)添加ch1102Activity.cs文件

    using Android.App;
    using Android.OS;
    using Android.Widget;
    
    namespace MyDemos.SrcDemos
    {
        [Activity(Label = "【例11-2】状态保存与恢复")]
        public class ch1102Activity : Activity
        {
            private int count = 0;
            protected override void OnCreate(Bundle savedInstanceState)
            {
                base.OnCreate(savedInstanceState);
                SetContentView(Resource.Layout.ch1102_Layout);
                var txtCount = FindViewById<TextView>(Resource.Id.textViewCount);
                if (savedInstanceState != null)
                {
                    count = savedInstanceState.GetInt("counter", 0);
                    txtCount.Text = $"你单击了按钮 {count} 次";
                }
                var btn1 = FindViewById<Button>(Resource.Id.btn1);
                btn1.Click += (s, e) =>
                {
                    count++;
                    txtCount.Text = $"你单击了按钮 {count} 次";
                };
            }
    
            protected override void OnSaveInstanceState(Bundle outState)
            {
                outState.PutInt("counter", count);
                base.OnSaveInstanceState(outState);
            }
        }
    }
  • 相关阅读:
    Python3.4 + Django1.7.7 搭建简单的表单并提交
    python3.4 + Django1.7.7 表单的一些问题
    TypeScript(10): String(同JS)
    TypeScript(09): Number(同JS)
    TypeScript(08): 循环
    TypeScript(07): 条件语句(同JS)
    TypeScript(06): 运算符(同JS)
    TypeScript(05): 变量声明
    TypeScript(04): 基础类型
    TypeScript(03):基础语法
  • 原文地址:https://www.cnblogs.com/rainmj/p/5204442.html
Copyright © 2020-2023  润新知