關於轉屏,與activity狀態的儲存
比較onSaveInstanceState() 與 onRetainNonConfigurationInstance()在不同需求中的用法
針對Android平台,不論出於哪種目的,都或多或少需要在多個Activities中的跳轉操作,其中包括為了獲得某些系統資源和必要資訊,而通過啟動(startActivity()&startActivityForResult() )Child Activity來提供一個選取器或者作為使用者輸入資訊的介質。這期間父級Activity將暫時性失去焦點,從而在這之前先通過 onSaveInstanceState() 方法臨時儲存一些必要的資訊,當父級的Activity重新成為當前焦點後,系統將觸發 onRestoreInstanceState() 恢複失去焦點前的原有資料!onRetainNonConfigurationInstance()也具有相同的目的來處理類似的請求,其主要是由於旋轉裝置而更改顯示模式,進而觸發這個方法的調用。
那麼在遇到某些特定需求時,特別是針對裝置旋轉後所導致的顯示模式發生變化後,應該依據什麼條件來判斷應用哪種方式才能更好的滿足需要呢?做出選擇之前有必要分別瞭解兩種方法的各自特點。
onSaveInstanceState()
在當前的Activity中通過新的Intent啟動其它Activities之後,它將通過這個方法自動儲存自身的資料,當再次出返回時可以通過 onRestoreInstanceState()複原資料。另外一種情況也將調用這個方法,當旋轉裝置後螢幕顯示模式發生改變時。需要注意的一點是整個過程完全由系統控制,無法通過onSaveInstanceState()返回一個自訂的資料。
另外,onSaveInstanceState()在所有Activity destroying過程中被調用,它僅僅是為了在重新回到這個特定的Activity之後,依據Activity state重新建立一個與之前狀態完全相同的Activity。例如:當我們啟動某些Connection時,State並不能依然儲存這個串連狀態。所以當調用onSaveInstanceState後,所有當前的connection將一同銷毀。當第二次通過 onRestoreInstanceState()找回之前的串連設定並重建立立新的串連實體。
如果大家有更多的發現,或者有不用於以上的驗證結果,非常感謝能參與這個話題的討論。
onRetainNonConfigurationInstance()
當Device configuration發生改變時,將伴隨Destroying被系統調用。通過這個方法可以像onSaveInstanceState()的方法一樣保留變化前的Activity State,最大的不同在於這個方法可以返回一個包含有狀態資訊的Object,其中甚至可以包含Activity Instance本身。新建立的Activity可以繼承大量來至於Parent Activity State資訊。
用這個方法儲存Activity State後,通過getLastNonConfigurationInstance()在新的Activity Instance中恢複原有狀態。
這個方法最大的好處是:
* 當Activity曾經通過某個網路資源得到一些圖片或者視頻資訊,那麼當再次恢複後,無需重新通過原始資源地址擷取,可以快速的載入整個Activity狀態資訊。
* 當Activity包含有許多線程時,在變化後依然可以持有原有線程,無需通過重新建立進程恢複原有狀態。
* 當Activity包含某些Connection Instance時,同樣可以在整個變化過程中保持串連狀態。
下邊是需要特別注意的幾點:
* onRetainNonConfigurationInstance()在onSaveInstanceState()之後被調用。
* 調用順序同樣介於onStop() 和 onDestroy()之間。
接下來將通過一個例子來簡單瞭解onRetainNonConfigurationInstance()和getLastNonConfigurationInstance()的用法。
這個例子將首先啟動一個包含兩個按鈕的Activity。其中一個按鈕用於調用本地通訊錄,並將所選擇的某一項作為傳回值傳給當前 Activity。另外一個按鈕的作用是查看當前所選擇的通訊資訊。正常的流程是當第一次啟動程式後,第二個查看資訊按鈕是不可用狀態。當通過Pick按鈕確定選擇並返回某一通訊錄內容時,查看資訊按鈕的狀態切換為可操作狀態。然後當改變裝置的Configuration時,可以注意到即便是 Activity通過onCreate()重新構建,但是之前所保證的UI屬性依然保持最後一次操作的狀態。
簡單建立一個包含兩個按鈕的UI:
?View Code XML
<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/pick"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:text="Pick"
android:enabled="true"
/>
<Button android:id="@+id/view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:text="View"
android:enabled="false"
/>
</LinearLayout>;
程式入口主程式碼如下所示(例子中包含了如何調用系統通訊錄的方法):
package com.androidres.karka;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.util.Log;
import android.provider.Contacts.People;
public class KarKa extends Activity {
static final int PICK_REQUEST=1337;
Button viewButton=null;
Uri contact=null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn=(Button)findViewById(R.id.pick);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Intent i=new Intent(Intent.ACTION_PICK,
People.CONTENT_URI);
startActivityForResult(i, PICK_REQUEST);
}
});
viewButton=(Button)findViewById(R.id.view);
viewButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
startActivity(new Intent(Intent.ACTION_VIEW, contact));
}
});
restoreMe();
viewButton.setEnabled(contact!=null);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode==PICK_REQUEST) {
if (resultCode==RESULT_OK) {
contact=data.getData();
viewButton.setEnabled(true);
}
}
}
@Override
public Object onRetainNonConfigurationInstance() {
return(contact);
}
private void restoreMe() {
contact=null;
if (getLastNonConfigurationInstance()!=null) {
contact=(Uri)getLastNonConfigurationInstance();
}
}
}