现在的位置: 首页 > 综合 > 正文

捕获Android运行时改变

2013年10月14日 ⁄ 综合 ⁄ 共 2951字 ⁄ 字号 评论关闭
      系统的一些设置可能在运行时发生改变(比如屏幕方向,键盘显示/隐藏,语言设置等)。当这种改变发生时,Android会重启当前Activity(先后调用onDestroy()和onCreate()方法)。重启Activity是为了使应用程序能够适应新的设置,并自动加载与新设置相关的资源。
      为了防止程序运行状态的丢失,在Activity重启过程中需要进行适当的处理。在Activity的生命周期中,系统会在Activity消失前调用onSaveInstanceState()方法,在该方法中我们可以保存程序当前的状态,然后在重启后的onCreate()或者onRestoreInstanceState()方法中恢复到之前保存的状态。
     但是有时候重新启动Activity时要恢复大量的数据,这会花费较大的代价,缺乏良好的用户体验。有两种方法可以解决这个问题:
        (1)设置改变时保存一个对象
        (2)允许Activity在设置改变时重新启动,但是会传递一个Object对象给新的Activity实例
1. 保存对象
     如果重启Activity需要恢复大量数据,重新建立网络连接,或者进行其他耗费资源的操作,在设置改变时完全重启Activity对用户来说可能太慢了。系统通过调用onSaveInstanceState()方法自动保存的Bundle对象可能不足以完全恢复Activity的运行状态——这种方法不是为了传递大的对象(比如位图)而设计的,并且这种方法传递的对象要进行序列化和逆序列化操作,耗费大量的内存,导致延长设置变更过程花费的时间。在这种情况下,我们可以通过一个Object对象保存与Activity运行状态相关的数据,减轻Activity的重启的负担。
     在设置改变时保存一个对象:
     重写onRetainNonConfigurationInstance()方法,返回我们想要保存的对象
     当Activity重新被创建时,调用getLastNonConfigurationInstance()恢复对象
     当Android系统因设置改变停止Activity时,它会在onStop()和onDestroy()两个回调函数之间调用onRetainNonConfigurationInstance()。在onRetainNonConfigurationInstance()方法中,我们可以返回任何Object对象,从而在设置改变后快速恢复Activity的状态。
     一个有价值的利用场景是:我们的应用程序在启动时从网络加载大量数据。如果用户改变了屏幕的方向导致Activity重启,重新从网络获取数据是比较慢的。我们可以实现onRetainNonConfigurationInstance()方法并返回一个对象,这个对象中包含了Activity已从网络加载的数据,并通过getLastNonConfigurationInstance()重新获取。例如:
@Override
public Object onRetainNonConfigurationInstance() {
    final MyDataObject data = collectMyLoadedData();
    return data;
}

      需要注意的是,虽然可以返回任何对象,但是我们应该避免返回那些与Activity绑定的对象,比如Drawable,Adapter,View,或者其他任何与Context相关联的对象。如何这样做了,会导致原Activity实例的所有视图和资源的泄露(无法被垃圾回收机制回收,占用内存资源)。
Activity再次启动时获取保存的数据:
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
 
    final MyDataObject data = (MyDataObject) getLastNonConfigurationInstance();
    if (data == null) {
        data = loadMyData();
    }
    ...
}
getLastNonConfigurationInstance()的返回值就是onRetainNonConfigurationInstance()的返回值。如果data为空(Activity不是因为设置的改变而启动时,会发生这种情况)这段代码会使用正常的流程初始化data对象。
2. 手动响应设置改变
    如果应用程序在某些设置改变时不需要更新资源,并且出于性能原因要避免Actvity的重启,我们可以声明由Activity自己来处理设置的变更。一般来说,不推荐使用这种方法,除非一定要避免因设置改变导致的Activity重启。
通过为AndroidManifest.xml文件中的元素增加android:configChanges属性来设置此Activity手动响应设置的改变,该属性的值是我们需要手动处理的相关设置。最常用的值是orientation(在屏幕方向改变时阻止Activity重启)和keyboardHidden(在键盘显示/消失时阻止Activity重启)。多个设置值可以同时进行声明,使用“|”进行分隔。
例如,下面的manifest代码声明一个Activity手动响应屏幕方向改变和键盘状态的改变:
<activity android:name=".MyActivity"
          android:configChanges="orientation|keyboardHidden"
          android:label="@string/app_name">

     此时,当声明中的某一个设置改变时,MyActivity不会重启,而是去调用onConfigurationChanged()回调方法。这个方法传递一个Configuration对象,指明了新的设置。通过读取Configuration,我们可以获得新的设置信息并据此进行一些适当的操作。
下面的代码示例中,onConfigurationChanged()检查了设备当前的屏幕方向设置:

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
 
    // Checks the orientation of the screen
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
    }
}

抱歉!评论已关闭.