Handling Runtime Changes

Some device configurations can change during runtime (such as screen orientation, keyboard availability, and language). When such a change occurs, Android restarts the running Activity (onDestroy() is called, followed byonCreate()). The restart behavior is designed to help your application adapt to new configurations by automatically reloading your application with alternative resources that match the new device configuration.

To properly handle a restart, it is important that your activity restores its previous state through the normal Activity lifecycle, in which Android calls onSaveInstanceState() before it destroys your activity so that you can save data about the application state. You can then restore the state during onCreate() oronRestoreInstanceState().

To test that your application restarts itself with the application state intact, you should invoke configuration changes (such as changing the screen orientation) while performing various tasks in your application. Your application should be able to restart at any time without loss of user data or state in order to handle events such as configuration changes or when the user receives an incoming phone call and then returns to your application much later after your application process may have been destroyed. To learn how you can restore your activity state, read about the Activity lifecycle.

However, you might encounter a situation in which restarting your application and restoring significant amounts of data can be costly and create a poor user experience. In such a situation, you have two other options:

  1. Retain an object during a configuration change

    Allow your activity to restart when a configuration changes, but carry a stateful object to the new instance of your activity.

  2. Handle the configuration change yourself

    Prevent the system from restarting your activity during certain configuration changes, but receive a callback when the configurations do change, so that you can manually update your activity as necessary.

Retaining an Object During a Configuration Change

If restarting your activity requires that you recover large sets of data, re-establish a network connection, or perform other intensive operations, then a full restart due to a configuration change might be a slow user experience. Also, it might not be possible for you to completely restore your activity state with the Bundle that the system saves for you with the onSaveInstanceState() callback—it is not designed to carry large objects (such as bitmaps) and the data within it must be serialized then deserialized, which can consume a lot of memory and make the configuration change slow. In such a situation, you can alleviate the burden of reinitializing your activity by retaining a Fragment when your activity is restarted due to a configuration change. This fragment can contain references to stateful objects that you want to retain.

When the Android system shuts down your activity due to a configuration change, the fragments of your activity that you have marked to retain are not destroyed. You can add such fragments to your activity to preserve stateful objects.

To retain stateful objects in a fragment during a runtime configuration change:

  1. Extend the Fragment class and declare references to your stateful objects.
  2. Call setRetainInstance(boolean) when the fragment is created.
  3. Add the fragment to your activity.
  4. Use FragmentManager to retrieve the fragment when the activity is restarted.

For example, define your fragment as follows:

public class RetainedFragment extends Fragment {

    // data object we want to retain
    private MyDataObject data;

    // this method is only called once for this fragment
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // retain this fragment
        setRetainInstance(true);
    }

    public void setData(MyDataObject data) {
        this.data = data;
    }

    public MyDataObject getData() {
        return data;
    }
}

Caution: While you can store any object, you should never pass an object that is tied to the Activity, such as a Drawable, an Adapter, a View or any other object that's associated with a Context. If you do, it will leak all the views and resources of the original activity instance. (Leaking resources means that your application maintains a hold on them and they cannot be garbage-collected, so lots of memory can be lost.)

Then use FragmentManager to add the fragment to the activity. You can obtain the data object from the fragment when the activity starts again during runtime configuration changes. For example, define your activity as follows:

public class MyActivity extends Activity {

    private RetainedFragment dataFragment;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // find the retained fragment on activity restarts
        FragmentManager fm = getFragmentManager();
        dataFragment = (DataFragment) fm.findFragmentByTag(“data”);

        // create the fragment and data the first time
        if (dataFragment == null) {
            // add the fragment
            dataFragment = new DataFragment();
            fm.beginTransaction().add(dataFragment, data”).commit();
            // load the data from the web
            dataFragment.setData(loadMyData());
        }

        // the data is available in dataFragment.getData()
        ...
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // store the data in the fragment
        dataFragment.setData(collectMyLoadedData());
    }
}

In this example, onCreate() adds a fragment or restores a reference to it. onCreate() also stores the stateful object inside the fragment instance. onDestroy() updates the stateful object inside the retained fragment instance.

Handling the Configuration Change Yourself

If your application doesn't need to update resources during a specific configuration change and you have a performance limitation that requires you to avoid the activity restart, then you can declare that your activity handles the configuration change itself, which prevents the system from restarting your activity.

Note: Handling the configuration change yourself can make it much more difficult to use alternative resources, because the system does not automatically apply them for you. This technique should be considered a last resort when you must avoid restarts due to a configuration change and is not recommended for most applications.

To declare that your activity handles a configuration change, edit the appropriate <activity> element in your manifest file to include the android:configChanges attribute with a value that represents the configuration you want to handle. Possible values are listed in the documentation for the android:configChanges attribute (the most commonly used values are "orientation" to prevent restarts when the screen orientation changes and"keyboardHidden" to prevent restarts when the keyboard availability changes). You can declare multiple configuration values in the attribute by separating them with a pipe | character.

For example, the following manifest code declares an activity that handles both the screen orientation change and keyboard availability change:

<activity android:name=".MyActivity"
          android:configChanges="orientation|keyboardHidden"
          android:label="@string/app_name">

Now, when one of these configurations change, MyActivity does not restart. Instead, the MyActivity receives a call to onConfigurationChanged(). This method is passed a Configuration object that specifies the new device configuration. By reading fields in the Configuration, you can determine the new configuration and make appropriate changes by updating the resources used in your interface. At the time this method is called, your activity's Resources object is updated to return resources based on the new configuration, so you can easily reset elements of your UI without the system restarting your activity.

Caution: Beginning with Android 3.2 (API level 13), the "screen size" also changes when the device switches between portrait and landscape orientation. Thus, if you want to prevent runtime restarts due to orientation change when developing for API level 13 or higher (as declared by the minSdkVersion andtargetSdkVersion attributes), you must include the "screenSize" value in addition to the"orientation" value. That is, you must decalare android:configChanges="orientation|screenSize". However, if your application targets API level 12 or lower, then your activity always handles this configuration change itself (this configuration change does not restart your activity, even when running on an Android 3.2 or higher device).

For example, the following onConfigurationChanged() implementation checks the current device orientation:

@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();
    }
}

The Configuration object represents all of the current configurations, not just the ones that have changed. Most of the time, you won't care exactly how the configuration has changed and can simply re-assign all your resources that provide alternatives to the configuration that you're handling. For example, because theResources object is now updated, you can reset any ImageViews with setImageResource() and the appropriate resource for the new configuration is used (as described in Providing Resources).

Notice that the values from the Configuration fields are integers that are matched to specific constants from the Configuration class. For documentation about which constants to use with each field, refer to the appropriate field in the Configuration reference.

Remember: When you declare your activity to handle a configuration change, you are responsible for resetting any elements for which you provide alternatives. If you declare your activity to handle the orientation change and have images that should change between landscape and portrait, you must re-assign each resource to each element during onConfigurationChanged().

If you don't need to update your application based on these configuration changes, you can instead notimplement onConfigurationChanged(). In which case, all of the resources used before the configuration change are still used and you've only avoided the restart of your activity. However, your application should always be able to shutdown and restart with its previous state intact, so you should not consider this technique an escape from retaining your state during normal activity lifecycle. Not only because there are other configuration changes that you cannot prevent from restarting your application, but also because you should handle events such as when the user leaves your application and it gets destroyed before the user returns to it.

For more about which configuration changes you can handle in your activity, see the android:configChangesdocumentation and the Configuration class.

有些设备配置可以在运行时改变(如屏幕方向,键盘可用性,和语言)。当这种变化发生时,Android的重新启动运行活动的onDestroy()被调用时,后面的onCreate() )。重新启动行为旨在帮助您的应用程序自动重新加载相匹配新的设备配置,替代资源应用程序适应新的配置。

要正确处理重新启动,但重要的是你的活动通过正常恢复以前的状态的活动周期,其中Android的调用 的onSaveInstanceState()它会破坏你的活动之前,这样就可以节省约应用程序状态的数据。然后,您可以在恢复状态的onCreate()onRestoreInstanceState()

要测试您的应用程序重新启动自身与应用程序的状态完好,应该调用配置更改(如更改屏幕方向),而在应用程序中执行各种任务。您的应用程序应该能够在任何时间,以处理事件,如配置更改,或者当用户的应用程序后,收到来电的电话,然后返回到你的应用程序更晚可能已经重新启动,而无需用户数据 ​​或状态的丢失销毁。要了解如何恢复活动状态,阅读有关的活动周期。

然而,你可能会遇到在重新启动应用程序并恢复显著的数据可能是昂贵的情况下,并创建一个不好的用户体验。在这种情况下,你还有其他两个选项:

  1. 配置变化时保持一个对象

    允许在配置改变你的活动重新启动,但携带状态的对象将活动的新实例。

  2. 处理配置改变自己

    从在某些配置更改重新启动活动防范系统,但收到一个回调时做配置变化,这样您就可以手动更新您的活动是必要的。

留住一个对象在配置变化

如果重新启动活动要求你恢复大型数据集,重新建立网络连接,或执行其他密集型操作,然后一个完整的重新启动由于配置改变可能是一个缓慢的用户体验。此外,它可能无法为你彻底恢复与您的活动状态,该系统节省了您的的onSaveInstanceState()回调它不是设计来进行内就必须大对象(如位图)和数据然后被序列化反序列化,这会消耗大量的内存和进行配置更改缓慢。在这种情况下,你可以缓解经重新初始化保留了您的活动的负担片段重新启动您的活动时,由于配置改变。这个片段可以包含你希望保留状态的对象引用。

当Android系统的活动关闭由于配置改变,你的活动,你已标记保留片段不被破坏。你可以这样的片段添加到您的活动,以保持状态的对象。

要运行时配置更改期间保留片段状态的对象:

  1. 扩展片段类,并宣布到你的状态的对象引用。
  2. 呼叫setRetainInstance(布尔)时创建的片段。
  3. 片段添加到您的活动。
  4. 使用FragmentManager重新启动活动时检索片段。

例如,定义您的片段如下:

公共  RetainedFragment  扩展 片段 { 

    我们想保留//数据对象
    私有 MyDataObject 数据; 

    //此方法只为这个片段调用一次
    @覆盖
    公共 无效的onCreate 捆绑savedInstanceState  { 
        的onCreate savedInstanceState ); 
        //保留本fragment 
        setRetainInstance ( true ); 
    } 

    public  void setData ( MyDataObject data )  { 
        this . data = data ; 
    } 

    public  MyDataObject getData ()  { 
        return data ; 
    } 
}

注意:虽然您可以存储任何对象,你不应该传递是联系在一起的一个对象的活动,如可绘制,一个适配器,一个视图 或与一个关联的其他对象上下文。如果你这样做,它会泄漏原来的活动实例的所有意见和资源。(泄漏资源意味着应用程序保持对他们的保留,他们不能被垃圾收集的,所以大量的内存可能会丢失。)

然后使用FragmentManager该片段添加到活性。在活动期间运行时配置的变化再次启动,您可以从片段中的数据对象。例如,定义您的活动如下:

     

     

    
      
        


        找到活动的保留片段 
        
 

        创建片段和数据在第一时间
        ,如果 dataFragment ==   { 
            //添加 
  
 
            从网络加载数据
            dataFragment 使用setData loadMyData ()); 
        } 

        //数据在dataFragment.getData()可
        ... 
    } 

    @覆盖
    公共 无效的onDestroy () { 
        的onDestroy (); 
        //存储在片段数据
        dataFragment 使用setData collectMyLoadedData ()); 
    } 
}

在这个例子中,的onCreate()增加了一个片段或恢复到它的基准。的onCreate()还存储所述片段的实例内的状态的对象。 的onDestroy()更新保留片段实例内的状态的对象。

处理配置改变自己

如果你的应用程序不需要特定的配置更改过程中更新的资源你有需要,您可以避开活动启动性能的限制,那么你可以声明你的活动处理的配置变化本身,它阻止系统重新启动活动。

注:处理配置更改自己可以使它更难以利用替代资源,因为系统不会自动为您应用这些。这种技术应被视为最后的手段,当你必须避免由于配置更改重新启动,因此不建议对大多数应用程序。

要声明的活动处理更改配置,编辑相应的<活动>在清单文件元素,包括安卓configChanges与代表要处理的配置属性的值。可能的值列在文档中的android:configChanges属性(最常用的值是“方向”,以阻止重新启动时屏幕方向的变化和“keyboardHidden” ,以防止重新启动时键盘的可用性的变化)。您可以通过将其与管道分开申报属性的多个配置值|字符。

例如,下面的清单代码声明,说明如何处理屏幕方向变化和键盘的可用性变化的活动:

<activity  android:name = ".MyActivity" 
          android:configChanges = "orientation|keyboardHidden" 
          android:label = "@string/app_name" >

现在,当这些配置的一个变化,MyActivity不会重新启动。而是,MyActivity接收呼叫到onConfigurationChanged() 。这种方法是通过一个配置对象,指定新的设备配置。通过阅读领域配置,可以判断新的配置,并通过更新在界面中使用的资源进行适当的更改。在这个方法被调用的时候,您的活动的资源对象更新返回基于新的配置资源,让您可以轻松重置您的UI元素没有系统重新启动的活动。

注意:与Android 3.2(API等级13)开始,在“屏幕尺寸”也随之变化时,纵向和横向之间的设备切换。因此,如果你想防止因方向改变运行时重新启动了API级别13或更高的开发时(由所申报的minSdkVersiontargetSdkVersion 属性),则必须包括“屏幕尺寸”除了值“方向”的价值。也就是说,你必须decalare 机器人:configChanges =“方向|屏幕尺寸”。但是,如果你的应用程序面向API级别12或更低,那么你的活动总是处理此配置变化本身(在Android 3.2或更高版本的设备上运行时,此配置更改不会重新启动您的活动,甚至)。

例如,下面的onConfigurationChanged()执行检查当前设备取向:

@Override 
public  void onConfigurationChanged ( Configuration newConfig )  { 
    super . onConfigurationChanged ( newConfig ); 

    //检查的方向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 (); 
    } 
}

配置对象表示当前所有的配置,而不仅仅是已经改变的人。大多数时候,你不会在乎究竟如何配置已发生变化,可以简单地重新分配所有资源提供替代品,你处理的配置。例如,由于资源对象现已更新,您可以重置任何的ImageView s的setImageResource() 和新配置相应的资源使用(如在描述提供了参考资料)。

请注意,从值配置字段是匹配的,从具体的常量整数配置类。对于文档中关于该常数随每个字段使用,请参阅相应的字段中配置的参考。

记住:当你声明你的活动,以处理配置更改,你有责任为重置您提供替代的任何元素。如果声明你的活动来处理方向变化,具有应在横向和纵向之间切换图像,必须在每个过程中的资源重新分配给每个元素onConfigurationChanged() 

如果您不需要更新基于这些配置更改您的应用程序,可以代替无法实现onConfigurationChanged() 。在这种情况下,所有的配置更改之前使用的资源仍然在使用,你才避免了您的活动的重新启动。然而,你的应用程序应该总是能够关闭和其以前的状态完好重新启动,所以你不应该考虑这种技术从正常活动的生命周期中保持你的状态逃跑。这不仅是因为有其他的配置更改,您无法重新启动您的应用程序阻止,而且还因为您需要处理事件当用户离开你的应用程序,例如,它被用户返回到之前销毁。

欲了解更多有关该配置更改您可以在活动的处理,看到安卓configChanges文件和配置 类。



你可能感兴趣的:(android,api,Runtime,resources,cha,handling,Accessing)