Android Developer : Handling Runtime Changes

文章翻译自Handling Runtime Changes
使用MarkDown编辑,根据自己的理解翻译Android Developer,深知有很多地方语序逻辑不当,希望能不断提升。翻译不当的地方,望指正

处理运行时变化

有些设备配置在运行时会发生变化(比如屏幕方向,键盘可用性和语言)。当发生这样的变化时,Android重启当前运行的ActivityonDestroy()被调用,紧跟着调用onCreate())。自动在程序中重新加载可以适应新配置的资源,这样可以帮助程序适应新的配置。

为了更好的处理重启,在正常的Activity生命周期中恢复它之前的状态是很重要的,Android在销毁Activity前调用onSaveInstanceState(),这样你可以存储有关程序状态的数据,你可以在onCreate()或者onRestoreInstanceState()恢复程序状态。

为了测试你的应用在自动重启时完整的程序状态,你应该调用配置变化(如改变屏幕方向)的同时在应用中执行多个任务。你的应用应该可以在任何时刻重启,并且没有用户数据和状态的丢失,是为了处理以下这种情况,比如配置变化或者用户接到一个电话,很久以后返回到你的程序,你的程序可能已经被销毁。要学习如何恢复你的Activity状态,请阅读Activity生命周期。

然而,你可能会遇到这样的情况,重启程序和恢复大量数据会很耗时,产生不好的用户体验。在这种情况下,你有两个其他的选择:

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

配置变化时允许你的Activity重启,但是要携带一个有变化的对象到新的Activity中。

b. 自己处理配置变化

为了防止某个配置变化时系统重启你的Activity,在配置变化时会接收到回调,这样你可以在必要的时候手动更新你的Activity.

配置变化时保持一个对象


如果重启你的Activity需要你恢复大量的数据,重新建立网络连接,或者执行其他密集操作,一个配置变化引起的完全的重启可能是一个不好的用户体验。而且可能你没有必要完全恢复你的Activity状态通过系统在onSaveInstanceState()为你保存的Bundle,这个并不是为了携带大量的对象(比如Bitmaps),而且其中的对象数据必须是可序列化和反序列化的,这些都会耗费大量的内存,使配置改变非常缓慢。当你的Activity因为一个配置变化要重启时,你可以通过持有一个Fragement来减轻重新初始化你的Activity的负担,这个fragment可以包含指向你想要保持的对象的引用。

运行时配置变化时,为了在fragment中持有变化的对象:

  1. 继承Fragement,声明指向变化对象的引用.
  2. fragment创建时调用setRetainInstance(boolean).
  3. 将fragment添加到你的activity中.
  4. 使用FragmentManager 来恢复fragment当activity重启.

例如,如下声明你的fragment:

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;
    }
}

注意:当你存储任何对象时,你不应该持有一个绑定在Activity的对象,比如Drawable,Adapter,View或者其他的和Context有关的对象。如果你这样做,就会泄露原始activity实例的所有视图和资源。(资源泄露的意思是你的应用总是持有他们,他们不能被垃圾回收,消耗很多内存)

然后使用FragmentManager添加fragment到activity中。当运行配置变化时,activity再次启动,你可以从fragment中获得数据对象。例如,如下定义你的activity:

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

在这个例子中,onCreate()添加一个fragment,或者回复对它的引用。onCreate()在fragment的实例中也存储了变化的对象。onDestroy()在获得的fragment实例中更新变化的对象

自己处理配置变化


如果你的程序在某种配置变化时不需要更新资源,或者有性能限制,要求你避免activity重启,你可以声明你的activity自己处理配置变化,可以防止系统重启你的activity。

注意:自己处理配置变化会使使用替代资源变得困难,因为系统没有自动为你调整这些。这种技术应该被认为是最后的方法,当你在配置变化时必须避免重启时,但是对于大多数的应用程序来说是不推荐的。

为了声明你的activity处理配置变化,在配置文件中编辑<activity>元素,添加android:configChanges属性,添加你想处理的配置变化的值。对于android:configChanges可添加的值在文档中被列出(最常使用的是orientation,防止屏幕方向变动时重启,keyboardHidden防止键盘可用性发生变化时重启).你可以声明多个配置属性值,通过|符号分隔开。

例如,下面的配置代码声明了一个activity,可以处理屏幕方向和键盘可用性的变化:

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

现在,当这其中的一个配置变化,MyActivity不会重启,这个MyActivity接收到一个回调onConfigurationChanged()。这个方法传递一个Configuration对象指定的是新的设备配置。通过阅读Configuration中的域,你可以决定新的配置,更新接口中的资源选择合适的变化。这个方法被调用的时候,你的activity的资源对象被更新为新的配置返回的资源,这样你可以很容易的重置你的界面中的元素,而不需要系统重启你的activity。

注意: 在Android3.2(API level 13)及以上,当设备在竖屏和横屏间切换的时候“screen size”也会发生变化。所以,如你也想在API level 13或以上的系统(由minSdkVersiontargetSdkVersion属性定义)防止运行时由于方向变化而重启,你必须将"screenSize"附加到"orientation",就是你必须声明android:configChanges="orientation|screenSize"。然而,如果你的应用目标是API level 12或更低的系统,你的activity总是自己处理配置变化(这个配置变化不会重启你的activity,甚至运行在Android3.2或更高的设备上)。

举个例子,下面的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();
    }
}

这个Configuration对象展示了当前所有的配置,并不仅仅是改变过的配置。在大多数的时候,你不会很关心这些配置如何改变的和如何简单重新分配所有资源用来提供给你当前处理的配置。例如,因为Resources对象已经更新 ,你可以通过使用setImageResource()重置任何的ImageView,适合新配置的资源就会被使用(如Providing Resources所描述的)。

请注意Configuration域中的值是整型,对应着Configuration中特定的常量。有关哪个常量和哪个域配合使用的文档中有说明。

记住:当你声明你的activity处理一个配置变化,你要对你重置任何元素负责。如果你声明你的activity处理方向变化,有需要在横屏和竖屏中变化的图片,你必须在onConfigurationChanged()中重新分配资源给元素。

如果你在配置变化的时候不需要更新你的应用,你可以不使用onConfigurationChanged()。在这种情况下,在配置变化之前所有的资源仍然在使用,你只需要避免activity重启。然而,你的应用应该可以关闭,可以重启回到之前完整的状态,所以你不应该认为这个技术是可以用来躲避在activity的生命周期中持有状态的方法。不仅因为有些配置变化你无法避免应用重启,而且你应该处理一些事件,如用户离开你的应用,在用户重新使用之前这个应用以及销毁了。

更多有关你可以在activity中处理的配置变化的信息,可以在android:configChanges文档和Configuration类中找到。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值