存储你的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应用程序存储不同的有效的类型了,并且当他们应该被使用时,你的用户永远不会丢失他们的数据,并且也有可能会有最好的用户体验。


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