|
標簽: 分類:C#、Android、VS2015; 創(chuàng)建日期:2016-02-21 一、簡介通過重寫(也叫回調)對應的方法來管理Activity的生命周期,比如用戶旋轉屏幕時應用程序要能自動保存和恢復實例的狀態(tài),這對于開發(fā)一個健壯而又靈活的應用程序而言至關重要。 1、本節(jié)要點 一旦真正理解了Activity的生命周期,就可以輕松自如地通過C#代碼去控制它了。這一節(jié)我們主要學習如何用Boundle存儲簡單類型的數(shù)據(jù)(比如int、double、string、bool、……等)。 當一個Activity停止或銷毀時,系統(tǒng)會存儲該Activity的狀態(tài),這個保存的狀態(tài)稱為實例狀態(tài)。在Activity的生命周期中,Android提供了存儲實例狀態(tài)的三個選項:
下面主要介紹前兩個選項。 2、利用Bundle保存和恢復場景(實例)的狀態(tài) 由于實例狀態(tài)保存在稱為Boundle的“key/value”字典中,因此,當創(chuàng)建一個Activty時,就可以在重寫的OnCreate()方法中傳遞boundle來恢復原來的實例狀態(tài)。 但是,不建議將圖像等復雜數(shù)據(jù)保存在boundle中,這是因為圖像不容易被串行化為“key/value”,這種情況下,應該將整個圖像文件作為一個簡單數(shù)據(jù)類型來看待(僅僅用字符串保存圖像的文件名或URL,而不是保存完整的圖像數(shù)據(jù))。 Bundle為Activity提供了“保存”和“恢復”實例狀態(tài)的兩個方法:
注:不要對“狀態(tài)”這個詞迷惑了,它的本質含義舉例來說就是:當活動A從可見變?yōu)椴豢梢?,然后再從不可見轉為可見后,活動A原來的數(shù)據(jù)、選項、……等“狀態(tài)”還需要在再次可見時重現(xiàn)嗎,如果需要重現(xiàn),就先在重寫的OnSaveInstanceState()方法中保存這些數(shù)據(jù)、選項、……等場景,然后再在重寫的OnRestoreInstanceState()方法中恢復這些場景。這樣一來,不管Android系統(tǒng)內部怎樣折騰,這些數(shù)據(jù)也不會丟了,仍然能在重新可見時重現(xiàn)原來的場景。 下圖演示了如何使用這兩個方法: (1)OnSaveInstanceState()方法 當一個Activity被Paused或者Stopped時,如果這個Activity對象仍然被保留在內存中,那么就可以簡單地通過Resume讓這個Activity返回到前臺。可是,當內存緊張時,系統(tǒng)可能會銷毀這個Activity,這樣程序就無法簡單地通過Resume還原這個Activity了。如果用戶還想返回到這個Activity,系統(tǒng)必須新建一個Activity讓用戶覺得原來的那個Activity還存在,可是如何還原到銷毀前的狀態(tài)呢?解決辦法就是讓系統(tǒng)在殺死那個Activity之前先調用OnSaveInstanceState()來保存原來的狀態(tài),這樣就可以用它去恢復了。 具體辦法是:在重寫的OnCreate()方法或者重寫的OnRestoreInstanceState()方法中從傳遞的Bundle中解析出保存的信息,并用它來恢復Activity原來的狀態(tài)。 如果原來并沒有儲存狀態(tài)信息,此時傳入的Bundle將為null,比如第1次創(chuàng)建Activity時就是這種情況。 總之,將Activity狀態(tài)完整地返回給用戶的兩種情況有: (a)如果原來的Activity對象還保存在內存中,系統(tǒng)就直接通過Resumed還原原來的狀態(tài); (b)如果原來的Activity對象已經不存在,系統(tǒng)就新建一個Activity,并檢查是否保存了原來的狀態(tài),如果保存了就還原它,否則將新建的狀態(tài)設置為null。 注意:系統(tǒng)不一定每次都在銷毀這個Activity之前調用OnSaveInstanceState()方法 ,比如用戶使用【Back】鍵關閉了這個Activity。在這種情況下,系統(tǒng)通常會在OnStop()方法之前或者在OnPause()之前調用OnSaveInstanceState(),而不是在銷毀前去調用它。 還有一些情況,比如在layout文件夾下的布局文件中聲明的控件,即使你不顯式調用OnSaveInstanceState()方法,系統(tǒng)仍然會默認將其狀態(tài)保存下來。但前提是你必須為想要保存其狀態(tài)的控件(widget)提供一個唯一的ID。如果不給widget指定ID,系統(tǒng)是無法保存它的狀態(tài)的。 另外,通過把android:saveEnabled 設置為"false",或者調用 SetSaveEnabled() 方法,也可以顯式地阻止layout中的某個view保存狀態(tài)。不過,通常不應該禁用保存,但是,假如你需要恢復activity UI的各個不同的狀態(tài),也許可以這么做。 盡管默認實現(xiàn)的 OnSaveInstanceState() 方法會保存activity UI的有用信息,但你仍然需要重寫(override)它來存入更多的信息。 例如,你可能需要保存在activity生命周期中改變的成員變量值(比如恢復UI的值,默認情況下,這些UI狀態(tài)的成員變量值是不會被恢復的)。 因為默認實現(xiàn)的 OnSaveInstanceState() 方法已經幫你保存了一些UI的狀態(tài),所以如果你為了保存更多的狀態(tài)信息而重寫此方法,那么在執(zhí)行自己的代碼之前應該確保先調用一次父類的 OnSaveInstanceState() 方法。同理,如果重寫 OnRestoreInstanceState(),也應該調用一次父類的該方法,這樣缺省的代碼就能正?;謴蛌iew的狀態(tài)了。 注意:因為 OnSaveInstanceState() 并不保證每次都會被調用,所以你應該只用它來記錄activity的一些臨時狀態(tài)信息(UI的狀態(tài))——千萬不要用它來保存那些需要長久保存的數(shù)據(jù)(比如那些需要存入數(shù)據(jù)庫里的數(shù)據(jù)),而是應該在用戶離開activity的時候在重寫的 OnPause()方法中來保存這些永久性數(shù)據(jù)。 一個檢測應用程序狀態(tài)恢復能力的好辦法就是旋轉設備,使屏幕方向發(fā)生改變(在模擬器中可通過按<Ctrl>+<F11>讓其旋轉)。當屏幕的方向改變時,因為要換用符合實際屏幕參數(shù)的資源,所以系統(tǒng)會銷毀并重建這個activity。正因如此,你的activity在被重建時能完整地恢復狀態(tài)非常重要,因為用戶可能會在使用應用程序時頻繁地旋轉屏幕。 本節(jié)的例子演示了如何在重寫的OnSaveInstanceState()方法中保存狀態(tài)數(shù)據(jù)。在這個例子中,用戶每單擊一次按鈕,都會將count的值加1,并在TextView中將count的值顯示出來。當改變配置狀態(tài)時--比如用戶旋轉屏幕時--會丟失count的值(此時boundle將會為null),重而寫該Activity的OnSaveInstanceState()方法即可保存count的值。這樣一來,當設備旋轉到一個新的狀態(tài)時(比如按<Ctrl>+<F11>將模擬器從縱向變?yōu)闄M向),由于count的值已經保存到boundle中,因此可通過下面的辦法重新獲取這個值: count = bundle.GetInt ("counter", -1); 注意:調用base.OnSaveInstanceState (outState)非常重要,這能確保始終存儲視圖的層次結構的所有原始狀態(tài)。 (2)OnRestoreInstanceState()方法 設備的某些配置可能會在運行時發(fā)生變化(比如屏幕方向、鍵盤可用性以及國家語言等狀態(tài)發(fā)生了變化)。當發(fā)生這些變化時,Android會重建這個運行中的activity,即:系統(tǒng)會先調用OnDestroy() ,再調用OnCreate()。這種設計有助于應用程序適用新的參數(shù)配置,措施就是通過把你預置的可替換資源(比如對應各種屏幕方向和尺寸的layout)自動重新裝載到應用程序中。 如果你采取了適當?shù)脑O計,讓activity能夠正確地處理這些因為屏幕方向而引起的重啟,并能如上面所述的那樣恢復activity的狀態(tài),那么你的應用程序將對生命周期中其它的意外事件更具適應能力。 處理這類重啟的最佳方式,就是在重寫的OnSaveInstanceState()方法中保存狀態(tài),在重寫的OnRestoreInstanceState()或者OnCreate()中恢復狀態(tài)。 這個例子是通過在重寫的OnCreate()方法恢復狀態(tài)的。 實際上,在許多情況下,并不需要總是重寫OnRestoreInstanceState()方法,因為大多數(shù)活動都可以在重寫的OnCreate()方法中直接恢復原來的狀態(tài)。 二、示例2—狀態(tài)保存和恢復此示例演示了如何保存和恢復用戶旋轉屏幕前后的狀態(tài)。 1、運行截圖 單擊按鈕3次的截圖如下: 按【Ctrl】+【F11】后的截圖如下:
再次按【Ctrl】+【F11】再次切換為縱向放置。 2、實現(xiàn)步驟 (1)添加ch1102_Layout.axml文件 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas./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】用于控制模擬器的屏幕【縱向/橫向】轉換,多次按它觀察屏幕旋轉后單擊按鈕的次數(shù)顯示的情況。" 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】狀態(tài)保存與恢復")] 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); } } } 【Android】11.2 通過重寫對應的方法保存和恢復實例的狀態(tài) 標簽: |
|
|