Android横竖屏切换时正确的Activity生命周期管理

使用过Android的用户大概都发现,Android系统在横竖屏切换的时候,应用程序的切换会比较慢。其实主要原因是在于横竖屏切换的时候,当前的Activity被销毁一次,再重新创建一次,因此会产生一定的迟滞感。

那Android为什么默认会有这样的行为?如何正确的管理Activity的生命周期?

先说一下Android为什么需要销毁当前的Activity,然后重新创建一次。其实这点也不难理解,Android的应用需要适应不同尺寸的屏幕,因此Android Project在res目录下可以针对不同情况,比如不同分辨率、不同dpi、横竖屏等等,存放不同的资源。也就是说,横屏或竖屏时可以采用不同的布局(layout)、不同的图片,由此甚至可以产生在横竖屏时界面完全不同的应用。但这些资源时需要在Activity创建的时候加载的,一旦Activity创建完成,想要重新加载不同的资源,只能重建Activity。

那如果Activity在横竖屏时不需要重新加载资源,难道就没有办法防止Activity的销毁和重建吗?

答案是可以,但我搜索了一下,发现很多中文资料中提供的办法都只能在Android 2.x上奏效,所以特地找了一下Android文档,找到了在Android 2.x到4.x都能奏效的方法。方法很简单,就是在Manifest文件里为Activity声明android:configChanges属性。该属性的意思是将configChanges中声明的事件交给Activity自身来处理,发生这些事件时Android不会对Activity进行销毁重建。当事件发生时,Android系统只会回调Activity上的onConfigurationChanged方法。

在Android3.2之前,只要声明android:configChanges="orientation|keyboardHidden"就可以防止Activity重建,但在Android3.2之后,横竖屏切换的时候,还会发生screen size change事件,因此就需要加入screenSize属性。

以下是防止Activity重建的代码示例:

      <activity
            android:name="com.example.lifecycletestonorientationchange.MainActivity"
            android:label="@string/app_name"
            android:configChanges="orientation|keyboardHidden|screenSize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

但果真这是最适合的解决方案吗?要回答这个问题,需要先看一下上述方案的前提,上述方法只有在Activity进行横竖屏切换时不需要重新加载资源才可行,不然Android系统不会为Activity重新加载资源。

如果需要重新加载Activity资源,有没有可行的优化方案?

如果需要重新加载资源,最方便的方法仍然时销毁Activity并重建,但在这个过程中可以进行一些优化。Activity在销毁和重建的过程会调用onSaveInstanceSate和onRestoreInstanceState方法去持久化和恢复一些状态,这个过程可能会比较耗时。是不是可以不需要这一过程?

Android在Activity中提供了onRetainNonConfigurationInstance方法,该方法中返回的状态对象,不会随Activity销毁,之后Activity重建时,就可以通过getLastNonConfigurationInstance将状态对象找回。需要警惕的是,onRetainNonConfigurationInstance方法不能返回Drawable,Adapter或者View对象,因为这些对象和Activity关系紧密,应当和Activity一起销毁重建,不然使用这种方法可能会造成严重的内存泄漏。

以下是示例代码:

    @Override
    public Object onRetainNonConfigurationInstance() {
        final MyStateObject state = getStateObject();
        return state;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final MyStateObject state = (MyStateObject) getLastNonConfigurationInstance();
        if (state == null) {
            state = loadMyState();
        }

    }

即便如此,仍然会有问题,原因在于Android api level 13就把onRetainNonConfigurationInstance标记为deprecated,(毕竟如果用错了该方法,会造成严重的内存泄漏,这不是Google愿意看到的),继续使用会有风险,那一天该方法被移除了,那继续使用该方法的应用会出现闪退的情况,因此需要使用替代的API。在Android文档中,该方法已被Fragment中的setRetainInstance(boolean)替代,所以建议大家在写Activity时,如果需要使用该功能时,最好都在Activity中加入Fragment。

猜你喜欢

转载自imagiczhang.iteye.com/blog/1901602