Xamarin Getting Started翻译系列二--Activity生命周期

来源:互联网 发布:淘宝卖qq号怎么卖 编辑:程序博客网 时间:2024/05/22 12:46

Activity生命周期

ActivityAndroid应用程序基本组成部分,具有多种不同的存在状态。Activity生命周期起始于实例化,结束与析构,期间包括多种状态。当Activity状态改变时,就会触发相应的生命周期事件,通知Activity将要发生状态变化,可以运行代码处理这些变化。本文讲解Activity生命周期并解释当状态变化时Activity应负责的事项,以便于实现体验良好功能可靠的应用程序。

Activities are a fundamental building block of Androidapplications and they can exist in a number of different states. The activitylifecycle begins with instantiation and ends with destruction, and includesmany states in between. When an activity changes state, the appropriatelifecycle event method is called, notifying the activity of the impending statechange and allowing it to execute code in order to adapt to that change. Thisarticle examines the lifecycle of activities and explains the responsibilitythat an activity has during each of these state changes in order to be part ofa well-behaved, reliable application.

概述Overview

AndroidActivity与常见程序概念不同。传统的应用程序开发通常具有一个启动应用程序的静态main函数。但对于Android确有所不同;Android应用程序可以使用应用程序注册的任意Activity来启动。实际上,多数应用程序都是用指定的Activity作为应用程序入口点。然而,如果应用程序崩溃,或被OS终止,OS会试图在应用程序最后开启的Activity或前一个Activity堆栈某处重启应用程序。另外,当应用程序不活动的时候会被OS暂停,缺少内存的时候会进行回收。为了让应用程序在重启等事件中正确的恢复状态数据,必须做细致的处理,尤其是Activity依赖于前一个Activity传递的数据。

Activitiesare an unusual programming concept specific to Android. In traditionalapplication development there is usually a static main method, which isexecuted to launch the application. With Android, however, things aredifferent; Android applications can be launched via any registered activitywithin an application. In practice, most applications will only have a specificactivity that is specified as the application entry point. However, if anapplication crashes, or is terminated by the OS, the OS can try to restart theapplication at the last open activity or anywhere else within the previousactivity stack. Additionally, the OS may pause activities when they’re notactive, and reclaim them if it is low on memory. Careful consideration must bemade to allow the application to correctly restore its state in the event thatan activity is restarted, especially if that activity depends on data fromprevious activities.

Activity生命周期实现为一系列生命周期中由OS自动调用的函数。开发者可以在这些方法中实现保存应用程序必须的状态和资源的功能。

应用程序开发者必须分析Activity的需求,确定需要实现哪些生命周期函数。如果处理不当,将导致应用程序不稳定、崩溃、资源泄露,甚至导致OS不稳定。

本文详细讲解Activity生命周期,包括:

Activity状态

生命周期方法

保存应用程序状态

本文同时包括图文教程讲解如何高效的在Activity生命周期中保存状态。读完本章后你应该可以理解Activity生命周期以及Android应用程序提供的支持。

Theactivity lifecycle is implemented as a collection of methods the OS callsthroughout the lifecycle of an activity. These methods allow developers toimplement the functionality that is necessary to satisfy the state and resourcemanagement requirements of their applications.

It isextremely important for the application developer to analyze the requirementsof each activity to determine which methods exposed by the activity lifecycleneed to be implemented. Failure to do this can result in applicationinstability, crashes, resource bloat, and possibly even underlying OS instability.

Thischapter examines the activity lifecycle in detail, including:

·        Activity States

·        Lifecycle Methods

·        Retaining the State of anApplication

This chapter also includes a walkthrough thatprovide practical examples on how to efficiently save state during the Activitylifecycle. By the end of this chapter you should have an understanding of theActivity lifecycle and how to support it in an Android application.

Activity 生命周期

AndroidActivity生命周期由Activity类提供的一系列函数组成,同时提供给开发者一个资源管理框架。借助这个框架开发者可对应用程序中每个Activity实现唯一的状态管理需求,以及适当的处理资源管理。

TheAndroid activity lifecycle comprises a collection of methods exposed within theActivity class that provide the developer with a resource management framework.This framework allows developers to meet the unique state managementrequirements of each activity within an application and properly handleresource management.

Activity状态

Android系统通过状态来识别Activity。这样Android就可以识别长时间不用的Activity,允许OS回收内存和资源。下图阐释Activity生命周期中的各个状态:

TheAndroid OS arbitrates Activities based on their state. This helps Androididentify activities that are no longer in use, allowing the OS to reclaimmemory and resources. The following diagram illustrates the states an Activitycan go through during its lifetime:


这些状态可以分为如下四种:

1.        ActiveRunning—如果Activity在最前面就认为是活动的或运行的,也就是在Activity栈的最顶点。也会Android中具有最高优先级的Activity,只能在极端情况下被OS杀死,如尝试获取超过设备可用内存大小的空间,将导致UI迟钝。

2.        暂停--当设备休眠,或Activity仍可见但部分被新的,非全屏或透明Activity所覆盖,这种Activity就是暂停的。暂停的Activity还是活的,他们维护所有状态和成员信息,并与窗体管理器关联。是ANdroid中第二高优先级的Activity,同时如为释放资源保证Active/Running Activity稳定和响应,只能由OS杀死他们

3.          Stopped/Backgrounded--Activity如果被其他Activity完全隐藏就认为是停止或后台状态。 停止的Activity还尽可能长的维护状态和成员信息,但停止的Activity是这三种状态中优先级最低的,比如,为了保证高优先级Activity的资源需要,OS将优先杀死这种状态的Activity

4.        Restarted -- Android可能随时会将暂停或停止的Activity移出内存。如果用户访问这些Activity,就必须重启,恢复以前保存的状态,然后显示给用户。

Thesestates can be broken into 4 main groups as follows:

1.      Active or Running -Activities are considered active or running if they are in the foreground, alsoknown as the top of the activity stack. This is considered the highest priorityactivity in Android, and as such will only be killed by the OS in extremesituations, such as if the activity tries to use more memory than is availableon the device as this could cause the UI to become unresponsive.

2.      Paused -When the device goes to sleep, or an activity is still visible but partiallyhidden by a new, non-full-sized or transparent activity, the activity isconsidered paused. Paused activities are still alive, that is, they maintainall state and member information, and remain attached to the window manager.This is considered to be the second highest priority activity in Android and,as such, will only be killed by the OS if killing this activity will satisfy the resource requirements needed to keep the Active/Running Activity stable and responsive.

3.      Stopped/Backgrounded - Activities that are completely obscured byanother activity are considered stopped or in the background. Stoppedactivities still try to retain their state and member information for as longas possible, but stopped activities are considered to be the lowest priority ofthe three states and, as such, the OS will kill activities in this state firstto satisfy the resource requirements of higher priority activities.

4.      Restarted –It is possible for an activity that is anywhere from paused to stopped in thelifecycle to be removed from memory by Android. If the user navigates back tothe activity it must be restarted, restored to its previously saved state, andthen displayed to the user.

响应配置变更重新创建Activity

为了进一步抽象,Android抛出一个叫做配置变更的概念。配置变更是Activity快速释放/重建的周期,当Activity的配置变化时,如设备旋转(Activity需要重建为宽屏或竖屏模式)、显示键盘(Activity需要调整大小),或悬停在其他Activity边上。

配置变更同时会引起与停止和重启Activity相同的状态变化。然而,当配置变更时为了提高应用程序的响应和效率,需要尽可能快的进行处理。因此,Android提供了特殊的API用于配置变更时来持久化状态。见ManagingState Throughout the Lifecycle节。

To make matters more complicated, Androidthrows one more wrench in the mix called Configuration Changes. Configurationchanges are rapid activity destruction/re-creation cycles that occur when theconfiguration of an activity changes, such as when the device is rotated (andthe activity needs to get re-built in landscape or portrait mode), when thekeyboard is displayed (and the activity is presented with an opportunity toresize itself), or when the device is placed in a dock, among others.

Configuration changes still cause the sameActivity State changes that would occur during stopping and restarting anactivity. However, in order to make sure that an application feels responsiveand performs well during configuration changes, it’s important that they behandled as quickly as possible. Because of this, Android has a specific APIthat can be used to persist state during configuration changes. We’ll coverthis later in the ManagingState Throughout the Lifecycle section.

Activity生命周期方法

AndroidSDK和扩展的XamarinAndroid框架提供了强大的模型来管理应用程序的Activity状态。当Activity状态发生变化,OS会调用Activity特定方法通知Activity。下图展示了与Activity生命周期相关的方法:

TheAndroid SDK and, by extension, the Xamarin.Android framework provide a powerfulmodel for managing the state of activities within an application. When anactivity’s state is changing, the activity is notified by the OS, which callsspecific methods on that activity. The following diagram illustrates thesemethods in relationship to the Activity Lifecycle:


作为开发者,需要在Activity中重写这些方法处理状态变化。这是非常重要的,然而,生命周期方法都是在UI线程中调用的,将会阻塞接下来的UI任务,如隐藏当前Activity,显示新的Activity,等等。因此,重写这些函数的时候,首先需要注意的就是执行效率。长时间运行的任务应该运行在后台线程。

下面讲述每个生命周期方法和他们的用处:

As adeveloper, you can handle state changes by overriding these methods within anactivity. It’s important to note, however, that all lifecycle methods arecalled on the UI thread and will block the OS from performing the next piece ofUI work, such as hiding the current activity, displaying a new activity, etc.As such, code in these methods should be as brief as possible to make anapplication feel well performing. Any long-running tasks should be executed ona background thread.

Let’sexamine each of these lifecycle methods and their use:

OnCreate

这是Activity创建后第一个被调用的方法。OnCreate总是被重写,执行启动Activity的初始化工作,如:

创建视图

初始化变量

将静态数据绑定到list

OnCreate带有一个Bundle类型的参数,在Activity之间存储状态信息、传递数据。如果bundle不空,就暗示Activity是重启的,需要从前一个实例恢复状态。下面代码阐述如果从bundle获取值:

This is the first method to be called whenan activity is created. OnCreate is always overridden toperform any startup initializations that may be required by an Activity suchas:

·        Creating views

·        Initializing variables

·        Binding static data to lists

OnCreate takes a Bundle parameter, which is a dictionary forstoring and passing state information and objects between activities If thebundle is not null, this indicates the activity is restarting and it shouldrestore its state from the previous instance. The following code illustrateshow to retrieve values from the bundle:

 

protectedoverridevoid OnCreate(Bundle bundle)

{

   base.OnCreate(bundle);

 

   string extraString;

   bool extraBool;

 

   if (bundle !=null)

   {

      intentString = bundle.GetString("myString");

      intentBool = bundle.GetBoolean("myBool");

   }

 

   // Set our view from the "main"layout resource   SetContentView(Resource.Layout.Main);

}

OnCreate调用完毕,Android将调用OnStart.

OnStart

这个方法在系统调用完OnCreate后调用。Activity可以重写这个方法来执行特定任务,如Activity可见前刷新Activity中视图的当前值。这个方法之后马上调用OnResume

This method is always called by the systemafter OnCreate is finished. Activities may override this method ifthey need to perform any specific tasks right before an activity becomesvisible such as refreshing current values of views within the activity. Androidwill call OnResume immediately after this method.

OnResume

系统已经开始与用户交互的时候,调用这个方法。Activity需要重新这个方法执行如下任务:

提升帧速率(游戏程序的常见任务)

启动动画

监听GPS更新

显示相关的alert或对话框

指定额外的事件处理函数

下面代码展示如何初始化摄像头:

Thesystem calls this method when the Activity is ready to start interacting withthe user. Activities should override this method to perform tasks such as:

·        Ramping up frame rates (a commontask in game building)

·        Starting animations

·        Listening for GPS updates

·        Display any relevant alerts ordialogs

·        Wire up external event handlers

As anexample, the following code snippet shows how to initialize the camera:

public void OnResume()

{

    base.OnResume(); // Always call the superclass first.

 

    if (_camera==null)

    {

        // Do camera initializations here

}

}

OnResume很重要,其中执行的动作、申请的资源,都在OnPause方法反向执行(停止执行、释放资源),这是在Activity暂停执行OnPause后,再次激活时唯一保证执行的生命周期方法

OnResume is important because any operation that is donein OnPause should be un-done in OnResume, sinceit’s the only lifecycle method that is guaranteed to execute after OnPause whenbringing the activity back to life.

OnPause

这个方法当系统将Activity变为后台运行,或Activity被部分覆盖时调用。Activity应该重写这个方法完成:

将未保存的数据持久化

释放或清理占用资源的对象

降低帧速率,停止动画

注销外部事件处理器或通知处理器(如,绑定到服务的)。为避免内存泄露,是必须做的

同样,如果Activity调用了对话框或alert,必须用.Dismiss()方法清理

例如,下面代码释放摄像头,因为Activity在暂停时不能在使用摄像头:

Thismethod is called when the system is about to put the activity into thebackground or when the activity becomes partially obscured. Activities shouldoverride this method if they need to:

·        Commit unsaved changes topersistent data

·        Destroy or clean up otherobjects consuming resources

·        Ramp down frame rates andpausing animations

·        Unregister external eventhandlers or notification handlers (i.e. those that are tied to a service). Thismust be done to prevent Activity memory leaks.

·        Likewise, if the Activity hasdisplayed any dialogs or alerts, they must be cleaned up with the .Dismiss()method.

As anexample, the following code snippet will release the camera, as the Activitycannot make use of it while paused:

1
2
3
4
5
6
7
8
9
10
11

publicvoidOnPause()

{

    base.OnPause(); // Always call the superclass first

 

    // Release the camera as other activities might need it

    if (_camera !=null)

    {

        _camera.Release();

        _camera =null;

    }

}

OnPause方法后可能有两个生命周期方法被调用:

1.        如果Activity返回前台会调用OnResume

2.        如果Activity被置于后台会调用OnStop

 

There are two possible lifecycle methodsthat will be called after OnPause:

1.      OnResume will be called if theActivity is to be returned to the foreground.

2.      OnStop will be called if the Activity isbeing placed in the background.

OnStop

这个方法当Activity不在对用户可见时调用。如下事件发生时会导致Activity不可见:

Activity启动了,覆盖了这个Activity

另一个Activity变为前台运行

这个Activity被释放

在缺乏内存的情况下OnStop方法可能不会调用,如Android缺少资源,不能正确的将Activity放到后台。基于这个原因,最好不要依赖Activity释放时调用OnStop方法。下一个可能调用的生命周期是如果Activity结束则调用OnDestroy,如果Activity变为可与用户交互则调用OnRestart

Thismethod is called when the activity is no longer visible to the user. Thishappens when one of the following occurs:

·        A new activity is being startedand is covering up this activity.

·        An existing activity is beingbrought to the foreground.

·        The activity is being destroyed.

OnStop may not always be called in low-memory situations,such as when Android is starved for resources and cannot properly backgroundthe Activity. For this reason, it is best not to rely on OnStop gettingcalled when preparing an Activity for destruction. The next lifecycle methodsthat may be called after this one will be OnDestroyif theActivity is going away, or OnRestart if the Activity is comingback to interact with the user.

OnDestroy

这是Activity实例在析构和完全从内存移除前的最后调用的方法。极端情况下,Android将杀死Activity所在的进程,但OnDestroy方法并没有被调用。多数Activity并不实现这个方法,而是将清理和关闭动作放在OnPauseOnStop中实现。通常在OnDestroy方法中清理可能导致资源泄露的长时间运行的资源,如在OnCreate方法中创建并置于后台运行的线程。

接下来没有生命周期方法继续执行了。

This is the final method that is called onan Activity instance before it’s destroyed and completely removed from memory.In extreme situations Android may kill the application process that is hostingthe Activity, which will result in OnDestroy notbeing invoked. Most Activities will not implement this method because mostclean up and shut down has been done in the OnPause and OnStop methods.The OnDestroy method is typically overridden to clean up longrunning resources that might leak resources. An example of this might bebackground threads that were started in OnCreate.

Therewill be no lifecycle methods called after the Activity has been destroyed.

OnRestart

这个方法当Activity被停止后又重新启动时调用。比如当应用程序中的Activity运行时用户按下了home键。这时会调用OnPauseOnStop方法,Activity被移到后台,但并没有释放。当用户在任务管理器中激活应用程序,Android将调用ActivityOnRestart方法。

没有什么通用必须在OnRestart中实现的逻辑。因为无论创建和重新启动Activity都会调用ActivityOnStart方法,Activity必须的资源都在OnStart方法中初始化,而不是在OnRestart中。

OnRestart方法之后调用的生命周期方法是OnStart

This method is called after your activityhas been stopped, prior to it being started again. A good example of this wouldbe when the user presses the home button while on an activity in theapplication. When this happensOnPause and then OnStop methodsare called, and the Activity is moved to the background but is not destroyed.If the user were then to restore the application by using the task manager or asimilar application, Android will call the OnRestart methodof the activity.

There are no general guidelines for whatkind of logic should be implemented in OnRestart. Thisis becauseOnStart is always invoked regardless of whether theActivity is being created or being restarted, so any resources required by theActivity should be initialized in OnStart, ratherthan OnRestart.

The next lifecycle method calledafter OnRestart will be OnStart.

Back键与Home

很多Android设备都有两个不同的按钮:Back键和Home键。下图是Android4.0.3的截图:

ManyAndroid devices have two distinct buttons: a “Back” button and a “Home” button.An example of this can be seen in the following screenshot of Android 4.0.3:


这两个按钮有微妙的区别,虽然按下后效果相同,都是将应用程序推送到后台。当用户按下Back按键,就是告诉Android Activity已经运行完毕。Android将销毁Activity。相反,用户按下Home键仅将Activity置于后台—Android不会杀死Activity

There isa subtle difference between the two buttons, even though they appear to havethe same effect of putting an application in the background. When a user clicksthe Back button, they are telling Android that they are done with the activity.Android will destroy the Activity. In contrast, when the user clicks the Homebutton the activity is merely placed into the background – Android will notkill the activity.

生命周期中管理状态

Activity停止或释放后,系统会提供时机来保存Activity状态以便于以后加载。保存状态指的是实例状态。Android提供三个选择在Activity生命周期中保存实例状态:

1.        将原生变量值保存到字典对象—Android常用来保存状态的Bundle对象

2.        定义含有复杂值如位图等自定义类。Android使用自定义类保存状态

3.        绕过配置变更生命周期,假设完全在Activity中维护状态

本文讲解前两种情况。

When anActivity is stopped or destroyed the system provides an opportunity to save thestate of the Activity for later rehydration. This saved state is referred to asinstance state. Android provides three options for storing instance stateduring the Activity lifecycle:

1.      Storing primitive values ina Dictionary known as a Bundle that Android will use to savestate.

2.      Creating a custom class thatwill hold complex values such as bitmaps. Android will use this custom class tosave state.

3.      Circumventing the configurationchange lifecycle and assuming complete responsibility for maintaining state inthe activity.

Thisguide covers the first two options.

Bundle State

第一种选择是使用叫做Bundle的字典对象已键值对的方式保存实例状态。Activity重新创建的时候调用OnCreate方法,会传递一个Bundle类型的参数,可用于恢复实例状态。不推荐使用Bundle持久化复杂数据,如位图这类不便于持久化为键值对的值;而是应该用于简单类型,如字符串。

Activity提供方法用于在Bundle中保存和读取实例状态:

OnSaveInstanceState –Activity释放的时候被调用。如果需要持久化键值对状态项,可以实现这个方法。

OnRestoreInstanceState –OnCreate方法调用完毕后,会调用这个方法,在初始化完毕后提供另一个机会加载Activity状态。

下图阐释如何使用这些方法:

The primary option for saving instancestate is to use a key/value dictionary object known as a bundle. Recall thatwhen an Activity is created that the OnCreate methodis passed a bundle as a parameter, this bundle can be used to restore theinstance state. It is not recommended to use a bundle for more complex datathat won’t quickly or easily serialize to key/value pairs (such as bitmaps);rather, it should be used for simple values like strings.

AnActivity provides methods to help with saving and retrieving the instance statein the Bundle:

·        OnSaveInstanceState – This is invoked by Android when the activity isbeing destroyed. Activities can implement this method if they need to persistany key/value state items.

·        OnRestoreInstanceState – This is called after the OnCreate methodis finished, and provides another opportunity for an Activity to restore itsstate after initialization is complete.

Thefollowing diagram illustrates how these methods are used:


OnSaveInstanceState

这个方法当Activity被停止的时候调用。这个方法带有一个Bundle参数用于存储Activity状态。当设备的配置改变时,Activity重写OnSaveInstanceState方法,使用其Bundle参数保存Activity的状态。例如如下代码:

This method will be called as the Activityis being stopped. It will receive a bundle parameter that the Activity canstore its state in. When a device experiences a configuration change, anActivity can use the Bundle object that is passed in to preservethe Activity state by overriding OnSaveInstanceState. For example, consider thefollowing code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

int c;

 

protectedoverridevoidOnCreate (Bundle bundle)

{

  base.OnCreate (bundle);

 

  this.SetContentView (Resource.Layout.SimpleStateView);

 

  var output =this.FindViewById<TextView> (Resource.Id.outputText);

 

  if (bundle !=null) {

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

  } else {

    c =-1;

  }

 

  output.Text= c.ToString ();

 

  var incrementCounter =this.FindViewById<Button> (Resource.Id.incrementCounter);

 

  incrementCounter.Click+= (s,e) => {

    output.Text = (++c).ToString();

  };

}

上例当按钮被点击时递增整数变量c,将结果显示在叫做outputTextView中。当设备状态变更时,例如设备旋转,将会丢失c的值,因为bundle总是null,如下所示:

The code above increments an integernamed c when a button named incrementCounter isclicked, displaying the result in a TextView named output. When aconfiguration change happens - for example, when the device is rotated - theabove code would lose the value of c because the bundle wouldbe null, as shown in the figure below:


为了保存c的值,Activity可以重写OnSaveInstanceState,在Bundle中保存值:

In order to preserve the value of c inthis example, the Activity can override OnSaveInstanceState, savingthe value in the bundle as shown below:

1
2
3
4
5

protectedoverridevoid OnSaveInstanceState (Bundle outState)

{

  outState.PutInt ("counter", c);

  base.OnSaveInstanceState (outState);

}

现在当设备旋转的时候,整数值将保存在Bundle中并用一行代码加载:

Now whenthe device is rotated to a new orientation, the integer is saved in the bundleand is retrieved with the line:

1

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

注意:调用基类的OnSaveInstanceState方法是很重要的,可保存控件层次状态。

Note: It isimportant to always call the base implementation of OnSaveInstanceState so that thestate of the view hierarchy can also be saved.

控件状态(View State)

重写OnSaveInstanceState在设备方向变化的时候保存持久化数据是很恰当的机制,如上例中保存计数。然而,OnSaveInstanceState的默认实现保存了每个UI控件的状态数据,比如每个控件的ID。例如,应用程序在XML中定义了一个EditText元素:

Overriding OnSaveInstanceState isan appropriate mechanism for saving transient data in an Activity acrossorientation changes, such as the counter in the above example. However, thedefault implementation ofOnSaveInstanceState will take care of savingtransient data in the UI for every view, so long as each view has an IDassigned. For example, say an application has an EditText elementdefined in XML as follows:

1
2
3
4

<EditText android:id="@+id/myText"

  android:layout_width="fill_parent"

  android:layout_height="wrap_content"/>

EditText控件指定了id,当用户输入文字并旋转设备,将如下所示显示数据:

Since the EditText controlhas an id assigned, when the user enters some data androtates the device, the data is still displayed, as shown below:


OnRestoreInstanceState

这个方法在OnStart之后调用,给Activity提供了一个时机,加载以前在OnSaveInstanceState方法保存到Bundle中的状态数据。这个函数与OnCreate相同,也传递一个Bundle参数。

下面的代码阐述如何在OnRestoreInstanceState方法中加载状态数据:

This method will be called after OnStart. Itprovides an activity the opportunity to restore any state that was previouslysaved to a Bundle during the previous OnSaveInstanceState. Thisis the same bundle that is provided to OnCreate,however.

The following code demonstrates how statecan be restored in OnRestoreInstanceState:

1
2
3
4
5
6

protectedoverridevoidOnRestoreInstanceState(Bundle savedState)

{

    base.OnRestoreSaveInstanceState(savedState);

    var myString = savedState.GetString(“myString”);

    var myBool = GetBoolean(“myBool”);

}

这个方法为加载状态提供了便利。有时在加载实例状态前更应该先初始化数据。另外,Activity的子类可能加载实例状态中的特定值。这时,没必要重写OnRestoreInstanceState方法,在OnCreate中加载状态就可以了。

Bundle中保存状态见Walkthrough - Saving the Activity state范例。

This method exists to provide some flexibilityaround when state should be restored. Sometimes it is more appropriate to waituntil all initializations are done before restoring instance state.Additionally, a subclass of an existing Activity may only want to restorecertain values from the instance state. In many cases, it’s not necessary tooverride OnRestoreInstanceState, since most activities can restore state using thebundle provided to OnCreate.

For an example of saving state usinga Bundle, refer to the Walkthrough - Saving the Activity state.

Bundle的限制

虽然OnSaveInstanceState 方法中可以方便的持久化数据,但有些限制:

并不是所有情况都调用。例如,按下homeback键退出Activity将不会调用OnSaveInstanceState

OnSaveInstanceState传递的参数不适合大数据类型,如图像。保存大对象需要在OnRetainNonConfigurationInstance函数中完成,下面将讨论。

使用Bundle保存数据,造成延时。

Bundle适用于无需大量内存的简单数据类型,然而非配置类型的数据(业务数据),通常都是复杂数据或获取数据代价较高的,比如Web Service调用或复杂数据库查询返回的数据。非配置类型的数据(业务数据)需要保存在一个对象中。下节介绍当配置变更时在OnRetainNonConfigurationInstance方法中存储复杂数据类型。

Although OnSaveInstanceState makesit easy to save transient data, it has some limitations:

·        It is not called in all cases.For example, pressing home or back to exit an Activity will not resultinOnSaveInstanceState being called.

·        The bundle passed into OnSaveInstanceState is not designed for large objects, such as images.In the case of large objects, saving the object from OnRetainNonConfigurationInstance is preferable, as discussed below.

·        Data saved by using the bundleis serialized, which can lead to delays.

Bundle state is useful for simple data thatdoesn't use much memory, whereas non-configuration instance data isuseful for more complex data, or data that is expensive to retrieve, such as froma web service call or a complicated database query. Non-configuration instancedata gets saved in an object as needed. The next section introducesOnRetainNonConfigurationInstance as a way of preserving more complex data typesthrough configuration changes.

实例化复杂数据

除了在Bundle中持久化数据,Android也可以重写方法OnRetainNonConfigurationInstance保存数据,并返回一个包含持久数据的Java.Lang.Object类型的实例。使用OnRetainNonConfigurationInstance 方法存储数据有两个好处:

OnRetainNonConfigurationInstance 方法返回的数据类型对大对象处理高效

OnRetainNonConfigurationInstance 方法在需要的时候才调用,对缓冲的利用更高效。

OnRetainNonConfigurationInstance 适用于保存获取很耗时的数据,例如Web Service返回的数据。下面代码为搜索Twitter的例子:

In addition to persisting data in thebundle, Android also supports saving data by overridingOnRetainNonConfigurationInstance and returning an instance of a Java.Lang.Object thatcontains the data to persist. There are two primary benefits of using OnRetainNonConfigurationInstance to save state:

·        The object returned from OnRetainNonConfigurationInstance performs well with larger, more complex data typesbecause memory retains this object.

·        The OnRetainNonConfigurationInstance method is called on demand, and only when needed.This is more economical than using a manual cache.

Using OnRetainNonConfigurationInstance is suitable for scenarios where it is expensive toretrieve the data multiple times, such as in web service calls. For example,consider the following code that searches Twitter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

publicclassNonConfigInstanceActivity : ListActivity

{

  protectedoverridevoid OnCreate (Bundle bundle)

  {

    base.OnCreate (bundle);

    SearchTwitter ("xamarin");

  }

 

  publicvoidSearchTwitter (string text)

  {

    string searchUrl =String.Format("http://search.twitter.com/search.json?"+"q={0}&rpp=10&include_entities=false&"+"result_type=mixed", text);

 

    var httpReq = (HttpWebRequest)HttpWebRequest.Create (newUri (searchUrl));

    httpReq.BeginGetResponse (new AsyncCallback (ResponseCallback), httpReq);

  }

 

  void ResponseCallback (IAsyncResult ar)

  {

    var httpReq = (HttpWebRequest)ar.AsyncState;

 

    using (var httpRes = (HttpWebResponse)httpReq.EndGetResponse (ar)) {

      ParseResults (httpRes);

    }

  }

 

  voidParseResults (HttpWebResponse httpRes)

  {

    var s = httpRes.GetResponseStream ();

    var j = (JsonObject)JsonObject.Load (s);

 

    var results = (from result in (JsonArray)j ["results"] let jResult = result asJsonObjectselect jResult ["text"].ToString ()).ToArray ();

 

    RunOnUiThread (() => {

      PopulateTweetList (results);

    });

  }

 

  void PopulateTweetList (string[] results)

  {

    ListAdapter=newArrayAdapter<string> (this, Resource.Layout.ItemView, results);

  }

}

代码从网上获取JSON格式的结果数据,并做解析,然后显示在list中,截图如下:

Thiscode retrieves results from the web formatted as JSON, parses them, and thenpresents the results in a list, as shown in the following screenshot:


当配置变化时,例如设备旋转了,代码会重复执行。为了重用前面获取的数据,避免重复的网络调用,可以重新OnRetainNonconfigurationInstance 方法保存结果,如下所示:

When a configuration change occurs - forexample, when a device is rotated - the code repeats the process. In order toreuse the originally retrieved results and not cause needless, redundantnetwork calls, we can useOnRetainNonconfigurationInstance to save the results, asshown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

publicclassNonConfigInstanceActivity : ListActivity

{

  TweetListWrapper _savedInstance;

 

  protectedoverridevoidOnCreate (Bundle bundle)

  {

    base.OnCreate (bundle);

 

    var tweetsWrapper =LastNonConfigurationInstanceasTweetListWrapper;

 

    if (tweetsWrapper !=null) {

      PopulateTweetList (tweetsWrapper.Tweets);

    } else {

      SearchTwitter ("xamarin");

    }

 

    publicoverrideJava.Lang.ObjectOnRetainNonConfigurationInstance ()

    {

      base.OnRetainNonConfigurationInstance ();

      return _savedInstance;

    }

 

    ...

 

    voidPopulateTweetList (string[] results)

    {

      ListAdapter=newArrayAdapter<string> (this, Resource.Layout.ItemView, results);

      _savedInstance =newTweetListWrapper{Tweets=results};

    }

}

现在当设备旋转,在LastNonConfiguartionInstance属性中获取以前的数据。本例中,Tweet的结果数据为string[].由于OnRetainNonConfigurationInstance方法需要返回Java.Lang.Object类型的结果,这里定义了Java.Lang.Object类型的子类,包含string[]成员,如下所示:

Now when the device is rotated, theoriginal results are retrieved from the LastNonConfiguartionInstanceproperty. In this example, the results consist of a string[] containingtweets. SinceOnRetainNonConfigurationInstance requires that a Java.Lang.Object bereturned, the string[] is wrapped in a class that subclasses Java.Lang.Object, asshown below:

1
2
3
4

classTweetListWrapper : Java.Lang.Object

{

  publicstring[] Tweets { get; set; }

}

如果试图将TextView作为OnRetainNonConfigurationInstance 方法返回的对象,将导致Activity内存泄露,如下代码所示:

For example, attempting to use a TextView asthe object returned from OnRetainNonConfigurationInstance will leak the Activity, asillustrated by the code below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

TextView _textView;

 

protectedoverridevoid OnCreate (Bundle bundle)

{

  base.OnCreate (bundle);

 

  var tv =LastNonConfigurationInstanceasTextViewWrapper;

 

  if(tv !=null) {

    _textView = tv;

    var parent = _textView.ParentasFrameLayout;

    parent.RemoveView(_textView);

  } else {

    _textView =new TextView (this);

    _textView.Text="This will leak.";

  }

 

  SetContentView (_textView);

}

 

publicoverrideJava.Lang.Object OnRetainNonConfigurationInstance ()

{

  base.OnRetainNonConfigurationInstance ();

  return _savedInstance;

}

本文中我们学习了如何在Bundle中保存简单状态数据,以及在OnRetainNonConfigurationInstance方法中持久化复杂数据类型。

In this section, we learned how to preservesimple state data with the Bundle, and persist more complex data typeswith OnRetainNonConfigurationInstance.

结论

Android Activity生命周期为应用程序的Activity状态管理提供了强大的框架支持,但有些难于理解和实现。本章节讲述了Activity不同生命周期中状态的保存,以及与状态相关的生命周期方法。接着讲述各个方法应该实现的逻辑。

TheAndroid activity lifecycle provides a powerful framework for state managementof activities within an application but it can be tricky to understand andimplement. This chapter introduced the different states that an activity may gothrough during its lifetime, as well as the lifecycle methods that areassociated with those states. Next, guidance was provided as to what kind oflogic should be performed in each of these methods.

演练 – 保存Activity状态

我们已经学习了Activity生命周期存储状态的理论知识,现在开始演练范例。

We have covered the theory behind saving state in theActivity Lifecycle guide; now, let’s walk through an example.

演练

打开ActivityLifecycle_Start项目并运行。这个项目很简单,只有两个Activity用来阐述Activity生命周期,以及各个生命周期函数的调用。

现在修改应用程序,声明一个计数变量记录按钮被点击次数。并在应用程序释放的时候将计数变量保存到实例状态中。

现在修改应用程序。

1.        MainActivity类添加一个成员变量:

int _counter = 0;

2.        重新如下方法:

protected override void OnSaveInstanceState (BundleoutState)

{

   outState.PutInt("click_count", _counter);

   Log.Debug(GetType().FullName,"Saving instance state");

 

   base.OnSaveInstanceState(outState);

}

这个函数将_counter 变量保存到Bundle中。

3.        接着,将布局文件 Resource/layout/Main.axml改为如下所示内容:

<?xml version="1.0"encoding="utf-8"?>

<LinearLayoutxmlns: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>

向布局中添加一个按钮,并显示用户点击次数。

4.        接下来修改OnCreate 代码,如下所示:

protected override void OnCreate(Bundle bundle)

{

   Log.Debug(GetType().FullName,"Activity A - OnCreate");

   base.OnCreate(bundle);

   SetContentView(Resource.Layout.Main);

   FindViewById<Button>(Resource.Id.myButton).Click += (sender, args)=> {

          var intent= new Intent(this, typeof(SecondActivity));

          StartActivity(intent);

   };

   if (bundle !=null)

   {

          _counter =bundle.GetInt ("click_count", 0);

          Log.Debug(GetType().FullName,"Recovered instance state");

   }

 

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

   };

}

代码实现内容较多,我们花几分钟来讲解。首先检查bundle是否为null,如果不是,则尝试获取键值为click_count的值。接着从布局中获取clickButton 控件并设置事件用户点击的时候更新_counter变量。

5.        现在需要在代码中保存Activity的实例状态,在MainActivity添加如下代码:

protected override void OnSaveInstanceState (BundleoutState)

{

   outState.PutInt("click_count", _counter);

   Log.Debug(GetType().FullName,"Saving instance state");

   base.OnSaveInstanceState(outState);

}

这段代码将_counter保存到实例状态中。

6.        运行程序,注意应用程序运行时输出信息。可看到应用程序运行时的Log.Debug消息。

到现在你应该已经对Activity生命周期和相关回调方法有了基本了解。

 

Open the project ActivityLifecycle_Start, and run it once. This is avery simple project that has two activities and will demonstrate the activitylifecycle and how the various lifecycle methods are called.

Let’schange the application so that we have a button that will count the number oftimes it was clicked. We will save the number of click counts to instance stateso that they are not lost if the application is destroyed.

Letsstart making changes to our application.

1.      Add an instance variableto MainActivity:

int _counter = 0;

2.      Next, override the method asshow in the following code snippet:

3.  protected override void OnSaveInstanceState(Bundle outState)

4.  {

5.     outState.PutInt("click_count", _counter);

6.     Log.Debug(GetType().FullName,"Saving instance state");

7.   

8.     base.OnSaveInstanceState(outState);

9.  }

Thiscode will save the value of the variable _counter tothe bundle.

10.  Next, edit the layout file Resource/layout/Main.axml to resemble the following XML:

11.       

12.      <?xml version="1.0"encoding="utf-8"?>

13.      <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"

14.          android:orientation="vertical"

15.          android:layout_width="fill_parent"

16.          android:layout_height="fill_parent">

17.          <Button

18.              android:id="@+id/myButton"

19.              android:layout_width="fill_parent"

20.              android:layout_height="wrap_content"

21.              android:text="@string/mybutton_text"/>

22.          <Button

23.              android:id="@+id/clickButton"

24.              android:layout_width="fill_parent"

25.              android:layout_height="wrap_content"

26.              android:text="@string/counterbutton_text"/>

27.      </LinearLayout>

Thisadds a new button to the layout – one that will display the number of times theuser has clicked the button.

28.  The next change we need to maketo our code is more significant. Change the OnCreate methodto resemble the following code:

29.       

30.      protected override void OnCreate(Bundlebundle)

31.      {

32.             Log.Debug(GetType().FullName,"Activity A - OnCreate");

33.             base.OnCreate(bundle);

34.             SetContentView(Resource.Layout.Main);

35.          FindViewById<Button>(Resource.Id.myButton).Click+= (sender, args) => {

36.                    varintent = new Intent(this, typeof(SecondActivity));

37.                    StartActivity(intent);

38.             };

39.             if(bundle != null)

40.             {

41.                    _counter= bundle.GetInt ("click_count", 0);

42.                    Log.Debug(GetType().FullName,"Recovered instance state");

43.             }

44.       

45.             varclickbutton = FindViewById<Button> (Resource.Id.clickButton);

46.             clickbutton.Text= Resources.GetString(Resource.String.counterbutton_text, _counter);

47.             clickbutton.Click+= (object sender, System.EventArgs e) => {

48.                    _counter++;

49.                    clickbutton.Text= Resources.GetString(Resource.String.counterbutton_text, _counter);

50.             };

51.      }

There isa lot happening in this code, so lets take a minute to explain what hishappening. First we check to see if the bundle parameter is null. If it isn’t,then we try to extract the value of the key click_count fromit.

Next wewire up some functionality to the clickButton inthe layout – we will update our _counterparameter each time the buttonis clicked by the user.

52.  With this code in place, theactivity now needs to update its instance state. Add the following method toMainActivity:

53.       

54.      protected override void OnSaveInstanceState(Bundle outState)

55.      {

56.             outState.PutInt("click_count", _counter);

57.             Log.Debug(GetType().FullName,"Saving instance state");

58.             base.OnSaveInstanceState(outState);

59.      }

Thiscode will save the value of the variable _counter tothe instance state.

60.  Run the application, and payattention to the application output as it is running. You should see theLog.Debug messagesappearing as the application is running.

At thispoint you should have a fundamental understanding of the Activity Lifecycle andthe callback methods.

总结

本节我们应用Activity生命周期的知识来保存状态数据。

In this walkthough, we have used our knowledgeof the Activity Lifecycle to preserve state data.
0 0
原创粉丝点击