APP Resource——Handling Runtime Change

一些设备的参数会在运行时改变(如屏幕方向,键盘是可用性,语言等),当这样的变化发生Android会重新启动当前正在运行的Activity(调用onDestroy()再调用onCreate())。重启动的设计是为了帮助你的程序通过自动加载匹配新的设备参数的资源文件来适应参数的变化。要恰当的处理重启动,需要确保你的activity能够通过一般的生命周期(通过在销毁activity之前调用onSaveInstanceState()保存相关数据)恢复它之前的状态。这样你就能在onCreate()或者onRestoreInstanceState()方法中恢复activity之前的状态。

为了测试你的app是否能毫发无损(数据不会丢失)的重启动,你应该在执行多种任务时让设备参数变化(如改变屏幕的方向)。为了处理诸如参数改变、用户接听电话后回到你的app(此时你的app进程可能已经被系统销毁)等事件,你的app必须能在任何时候重启动,而且不能丢失用户数据或者状态。学习如何恢复你Activity状态,请阅读Activity lifecycle。

         然而你可能会遇到重启动app并且恢复重要数据需要耗费较多的系统资源,从而导致用户体验变差的情况。在这样的情况下,你有两个选择。

         a在设备参数改变期间保持一个对象

           允许你的activity在参数改变时重启动,但是携带一个有状态的对象给你的activity的新实例。

         B自己处理设备参数的改变

           阻止系统在特定的参数改变的时候重启动你的activity,但是在设备参数改变时通过回调方式使你能够根据需要手动的更新你的activity

Retaining an Object During a Configuration Change

         如果重启动你的activity需要你恢复大量的数据,如重新建立一个网络连接,或者执行其他耗时较大的操作时,那么一个因参数改变而完全地重启动会使用户体验变慢。而且,你想要用Bundle(系统通过onSaveInstanceState()回调函数帮你存储)完全恢复你的activity数据也不太可能,因为Bundle不是为了携带大量数据(如bitmaps)而设计的。而且在Bundle中的数据必须经过序列化和反序列化操作,这通常会耗费大量的存储空间并且使app的反应变慢。在这样的情况下,可以通过在你的activity由于设备参数改变而重启时,保持一个Fragment减少activity重新初始化的负担。这个fragment可以包括你想保持的状态性的对象的引用。

         当Android系统因为设备参数改变而关闭你的activity时,activity中你标记要保持的的fragment不会被销毁。你可以为你的activity添加这样的fragment来保存状态性的对象。

         为了在运行参数变化时在fragment保持状态性的东西:

         1继承fragment类并声明对你的状态性的东西的引用。

         2当fragment被创建时,调用setRetainInstance(boolean)

         3把fragment添加到你的activity

         4用FragmentManager来在activity重启动时重新获得fragment

         作为例子,像下面这样定义你的fragment

public class RetainedFragment extends Fragment {

    // data objectwe want to retain
    private MyDataObject data;

    // this methodis 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相关联的对象,如Drawble,Adapter,View或者其他与Context有联系的对象。不然,它会泄露原始activity实例的所有views和资源(泄露资源意味着你的app一直保持着他们,就不能被垃圾回收,这样会导致大量的空间浪费)。

 

然后使用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或者回复fragment的引用,onCreate()也可以在fragment实例中存储状态性的对象。onDestroy()在保持的fragment实例中更新状态性对象。

 

Handling theConfiguration Change Yourself

如果你的app不需要在指定的参数改变时更新资源时,你可以用一个操作限制来避免activity的重启,然后声明你的activity自己处理参数改变事件,这样就可以阻止系统重启activity。
注意:自己处理参数改变的事件会使使用alternative resources更困难,因为系统不会自动帮你应用他们。这个技术应该作为最后的手段,对大部分的app而言不推荐使用。
为了声明你的activity自己处理参数改变的事件,你可以编辑mainifest.xml中对应的<Activity>标签中的android:configChanges属性,设定你想自己处理的参数。可能的值在该属性的文档中列出(最常见的是“orientation”来阻止屏幕方向改变时重启activity,以及“keyboardHidden”来阻止键盘可用性改变时的重启)。你可以在android:configChanges声明多个参数值,用|隔开。
例如,下面的声明使activity自己处理屏幕方向改变和键盘可用性的事件
<activity android:name=".MyActivity"
          android:configChanges="orientation|keyboardHidden"
          android:label="@string/app_name">
现在,当这些设置过的参数改变时,MyActivity不会重启动。当这些参数改变时,MyActivity的onConfigurationChanged()会被调用,这个函数传递一个包含新参数的Configuration对象。通过读取Configuration对象中域的值,你可以获取新的参数并自己通过更新资源来做适当的改变。在这个方法被调用时,你的acitivity的资源对象已经根据新的参数数更新了资源并将其返回,这样你就可以容易的重新设置你界面中的ui元素而不用系统重启动你的activity。
注意:从Andorid 3.2(level 13) 开始,在设备在横屏和竖屏之间改变时,屏幕尺寸也会跟着改变。因此,在开发所用api level(在minSdkVersion和targetSdkVersion属性中声明)为13或更高时,如果你想要阻止因为屏幕方向的变化而导致的重启动,你必须在 orientation之外在android:configChanges属性的值中包括 screenSize
,这意味着你必须声明 android:configChanges="orientation|screenSize"。然而,如果你的android api level是12或更低 时,则不需要包括screenSize(当你的app在更高版本的api上运行时系统也不会重启动)。
例如,下面的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对象现在给更新,你可以通过setImageResources设置任何的ImageView,而且适配新参数的恰当的资源被使用。
注意到Configuration对象域是整形变量,并且使存储特定的常量。想知道这些变量匹配那些常量,请查阅api。
记住:当你声明你自己处理参数变化时,你要重新设置任何你有提供选择性资源的元素。如果你声明你的activity自己处理屏幕方向的变化并且你有屏幕在水平方向和竖直方向上显示的资源时,你必须在onConfigurationChanged()中给每一个元素重新安排资源。
如果你不需要根据某些设备参数变化而更新你的app,你可以不实现onConfigurationChanged()函数。在这样的情况下,所有的资源都没有改变,而是避免了重启动。然而,你的activity应该总是能被销毁并原封不动的重启动,所以你不要认为这项技术是为了避免保持设备状态的操作。这不仅是因为总有其他的你不能阻止重启动的参数改变,也因为你要处理 当用户离开你的app,然后在用户回来之前,你的app进程被系统销毁的情况。
For more about which configuration changes you can handle in your activity, see the android:configChanges  documentation and the  Configuration  class.


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值