存儲你的android應用程序的activity的狀態

​原文地址:http://www.javacodegeeks.com/2014/11/storing-the-state-of-an-activity-of-your-android-application.html

這是我最近在我的關於在android應用程序中保存數據的系列文章。前面發表過的在你的應用程序中保存數據的多種方式的文章:

Introduction : How to save data in your Android application

Saving data to a file in your Android application

Saving preferences in your Android application

Saving to a SQLite database in your Android application


最後這篇文章將解釋你應當什麼時候保存你的應用程序當前的狀態以保證你的用戶不會丟失他們的數據。有兩種狀態能被保存:如果用戶在輸入數據的時候被中斷,你能保存當前UI的狀態,以至於在用戶重新啓動應用程序時可以恢復,或者你能把數據保存到一個更持久的數據存儲來在任何時候訪問它。


保存當前UI的狀態


當其它應用程序被啓動的時候,會隱藏你的應用程序,你的用戶從輸入的數據開始也許一直還沒有把數據準備保存到一個持久的數據存儲中。另一個方面,例如有電話來的時候,你應當任然保存activity的當前狀態,以至於用戶不會丟掉他們所有的工作。android設備的屏幕被旋轉了也會有這樣的影響,一次這也是個好保存狀態的好的原因。


當上面所說的事件或者另一些事件要求保存activity的狀態出現的時候,android SDK調用當前activity的onSaveInstanceState方法,這個方法接收一個android.os.Bundle對象作爲一個參數。如果你使用來自於Android SDK的標準視圖並且那些視圖有唯一標識,那些控制的狀態將會自動的被保存到bundle中去。但是如果你使用一個有相同標識的視圖的多個實例,例如通過重複使用一個ListView視圖,在你的控制中被輸入的值由於標識重複將不會被保存。還有,如果你創建了你自定義的控制,你將必須保存那些控制的狀態。


如果你需要手動的保存狀態,你必須覆蓋onSaveInstanceState方法,並且把你自己的信息添加到作爲一個使用鍵值對的參數被接收的android.os.Bundle中。這個信息在以後將是有效的在這個activity的onCreate和onRestoreInstanceState方法中需要被修復。所有原始類型或者那些類型值的數組能被保存到bundle中。如果你想保存對象或者對象數組到這個bundle中,他們必須實現java.io.Serializable或者android.os.Parcelable接口。


爲了展示保存的狀態,我將使用一個在文章saving to a database中使用的升級版的應用程序,這個程序在GitHub上http://github.com/CindyPotvin/RowCounter是可以使用的。這個應用程序管理行計數器用於knitting工程,但是它沒有方式創建一個工程。在新版本中,用戶現在能創建一個新工程,並且如果用戶在創建工程的時候中斷了,那麼工程的狀態將被創建來保存狀態。對於展示的目的,用數字表示的值被輸入用於一個自定義的CounterView控制,這個視圖不保存狀態,因此我們必須手動的保存每個計數器的狀態到bundle中。

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
   CounterView rowCounterAmountView = (CounterView)this.findViewById(R.id.row_counters_amount);
   savedInstanceState.putInt(ROW_COUNTERS_AMOUNT_STATE, rowCounterAmountView.getValue());

   CounterView rowAmountView = (CounterView)this.findViewById(R.id.rows_amount);
   savedInstanceState.putInt(ROWS_AMOUNT_STATE, rowAmountView.getValue());

   // Call the superclass to save the state of all the other controls in the view hierarchy
   super.onSaveInstanceState(savedInstanceState);
}

當用戶導航返回到應用程序時,這個activity從這些被保存在bundle中的信息中被Android SDK自動創建。在那個點上,你也必須給你的自定義控制重新恢復UI的狀態。你能使用你保存再bundle中的數據從你的activity的兩個方法中恢復UI的狀態:當這個activity被重新創建的時候或者onRestoreInstanceState方法被調用的時候,onCreate方法首先被調用,然後是onStart方法。你能在一個方法或者其它的方法中恢復狀態,它大多數情況下都不會出問題,但是萬一在onCreate和onStart方法被執行之後一些初始化需要被執行,那就會有問題。下面是從activity的bundle中保存的恢復狀態的例子:

@Override
protected void onCreate(Bundle savedInstanceState) {
   [...Normal initialization of the activity...]

   // Check if a previously destroyed activity is being recreated.
   // If a new activity is created, the savedInstanceState will be empty
   if (savedInstanceState != null) {
      // Restore value of counters from saved state
      CounterView rowCounterAmountView;
      rowCounterAmountView  = (CounterView)this.findViewById(R.id.row_counters_amount);
      rowCounterAmountView.setValue(savedInstanceState.getInt(ROW_COUNTERS_AMOUNT_STATE));

      CounterView rowAmountView = (CounterView)this.findViewById(R.id.rows_amount);
      rowAmountView.setValue(savedInstanceState.getInt(ROWS_AMOUNT_STATE));
   }
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
   // Call the superclass to restore the state of all the other controls in the view hierarchy
   super.onRestoreInstanceState(savedInstanceState);

   // Restore value of counters from saved stat
   CounterView rowCounterAmountView = (CounterView)this.findViewById(R.id.row_counters_amount);
   rowCounterAmountView.setValue(savedInstanceState.getInt(ROW_COUNTERS_AMOUNT_STATE));

   CounterView rowAmountView = (CounterView)this.findViewById(R.id.rows_amount);
   rowAmountView.setValue(savedInstanceState.getInt(ROWS_AMOUNT_STATE));
}


記住,在一個bundle中保存數據並不意味着會作爲一個參數被保存,因爲它僅僅存儲的是視圖當前的狀態:它不是activity生命週期的一部分,並且僅僅是當activity需要被重新創建的時候或者被置於後臺時纔會被調用。當應用程序被銷燬,因而這個activity的狀態可能永遠不會被恢復,這就意味着onSaveInstanceState方法不會被調用。爲了保存數據,應當永遠不能丟失你應當保存到持久數據倉庫中的數據,這些在早期系列中被描述了。但是當這個數據應當被保存的時候呢?


保存你的數據到一個持久數據倉庫中

當activity無緣由的被置於後臺或者被銷燬,如果你需要保存數據到持久數據倉庫中時,你必須在activity的onPause方法中保存你的數據。如果UI完全的隱藏了,這個onStop方法纔會被調用,因此你不能一直依賴它被喚起。所有必要的數據在這時候必須被保存,因爲在以後發生的事情中你沒有控制:例如用戶也許殺掉應用程序並且數據將丟失。


在前面的應用程序版本中,當用戶在一個工程增加行計數,應用程序每次把當前的計數器的值保存到數據庫中。現在只有當用戶退出activity時我們將保存這些數據,並且在每一個計數器不再被請求時保存:

@Override
public void onPause() {
   super.onPause();  
   
   ProjectsDatabaseHelper database = new ProjectsDatabaseHelper(this);
   
   // Update the value of all the counters of the project in the database since the activity is
   // destroyed or send to the background
   for (RowCounter rowCounter: mRowCounters) {
      database.updateRowCounterCurrentAmount(rowCounter);
   }


稍後,如果你的應用程序被銷燬了並且用戶訪問再次訪問這個activity,兩種可能的處理出現依賴於Android OS如何持有你的activity。如果activity任然在內存中,例如,如果用戶打開另一個應用程序,並且立即返回,那麼onRestart方法首先會被調用,緊接着onStart方法會被調用,最後onResume方法被調用,然後這個activity就展現給用戶了。但是如果activity被回收了並且正在被重新創建,例如,如果用戶翻轉了設備以至於佈局重新被創建,會創建一個相同的新的activity:onCreate方法首先被調用,緊接着會調用onStart方法,最後onResume方法被調用,並且activity被展現給用戶。

因此,如果你想使用這些被存在一個持久數據倉庫中的數據,在你的丟失狀態的activity中初始化控制,你應當把代碼放在onResume方法中,因爲不管是否這個activity重新被創建,它總是被調用。在前面的例子中,顯示的存儲數據不是必須的,因爲不是使用自定義控制:如果這個activity被回收,它會從中斷的那個點被重新創建,並且onCreate方法從數據庫中的數據初始化控制。如果activity任然在內存中,那不會有其它的事情會做。當這些值首先被戰士的時候,Android SDK持有這些值,在前面已經解釋了保存UI狀態的段落。這是在onCreate方法中提醒發生了什麼事情:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.project_activity);
   Intent intent = getIntent();
   long projectId = intent.getLongExtra("project_id", -1);
   // Gets the database helper to access the database for the application
   ProjectsDatabaseHelper database = new ProjectsDatabaseHelper(this);
   // Use the helper to get the current project
   Project currentProject = database.getProject(projectId);

   TextView projectNameView = (TextView)findViewById(R.id.project_name); 
   projectNameView.setText(currentProject.getName());
     
// Initialize the listview to show the row counters for the project from 
// the database
   ListView rowCounterList = (ListView)findViewById(R.id.row_counter_list);
   mRowCounters = currentProject.getRowCounters();
   ListAdapter rowCounterListAdapter = new RowCounterAdapter(this,
                                                             R.layout.rowcounter_row,
                                                             currentProject.getRowCounters());
   rowCounterList.setAdapter(rowCounterListAdapter);
   }


這篇文章包含了在你的應用程序中保存數據的系列。你現在知道給Android應用程序存儲不同的有效的類型了,並且當他們應該被使用時,你的用戶永遠不會丟失他們的數據,並且也有可能會有最好的用戶體驗。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章