设备旋转后,onCreate(...)方法被调用两次

12

我有一个关于旋转Android设备的问题。 我的代码在onCreate(...)中记录了一个静态和非静态属性。

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {
    static int sn;
    int n;

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

        sn++;
        n++;

        Log.i("onCreate", String.format("sn=%d n=%d", sn, n));
    }
}

屏幕方向为竖屏。当我第一次运行代码时,我收到了以下信息:

onCreate(): sn=1 n=1

我将屏幕旋转为横向后,出现了:

onCreate(): sn=2 n=1

我再次将屏幕旋转为竖屏后,出现了这个问题:

onCreate(): sn=3 n=1
onCreate(): sn=4 n=1

我的问题是:

  1. 当设备旋转回竖屏时,如何防止onCreate(...)被调用两次?
  2. 当设备旋转时,如何保存非静态变量的值?

https://dev59.com/inRB5IYBdhLWcg3w9Lzn - Sameera Chanaka
6个回答

32
这是模拟器中已知的问题(请参见此处的讨论)。这不是一个错误,但多年来它一直困扰着许多人。据我所知,基本问题是当您的活动进入纵向模式时,模拟器会单独处理两个配置更改:方向更改本身和键盘的停用。因此,您的活动会重启两次。在另一个方向上不会发生,因为没有与键盘激活对应的配置更改事件。(我觉得这很奇怪,但显然出于某种原因,这是所需的行为)。
无论如何,解决方案似乎是将以下内容添加到您的活动清单声明中:
android:configChanges="keyboardHidden|orientation"

如果您确实需要这些更改来加载新资源,您可以在onConfigurationChanged中手动处理它。


1
+1,有趣。我甚至从未注意到它被调用了两次(或者是否重要)。 - Dave Newton
+1:在模拟器上注意到了这个问题,但从1.6/2.x时代开始就没有在真实设备上测试过,所以并没有太在意。自那以后就再也没有依赖于模拟器了。 - Pedantic
3
我必须使用这个来覆盖所有情况:keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize - dtbarne
android:configChanges="keyboard|keyboardHidden|orientation|screenSize" 我已添加。谢谢。 - iroiroys
不仅在模拟器上,它在真实设备上的表现也是一样的,而且,configChanges 对我也没用 :( - Narendra Singh

10
无论何时屏幕方向发生变化,该Activity都会被销毁并通过onCreate()方法启动一个新的Activity。因此每次旋转屏幕时,该Activity都会被销毁,并通过onCreate()方法启动新的Activity。 您可以通过重写onSaveInstanceState(Bundle b)方法来保存非静态成员变量。Android在每次屏幕旋转时调用此方法,并将给定的Bundle b传递给oncreate(Bundle b),您可以从中提取您的非static成员变量。

我重写了 onSaveInstanceState(Bundle savedInstanceState) 方法,但当我旋转模拟器时它并没有被调用。 - wannik
您可能没有正确地覆盖该方法,它会在onPause()之前被调用。 - Aakash
@Override protected void onSaveInstanceState(Bundle bundle1){
super.onSaveInstanceState(bundle1); Log.i(tag,"在 onSaveInstanceState 方法内"); bundle1.putInt("变量 n",n); bundle1.putInt("sn",sn); }
- Aakash
我知道为什么我无法在onSaveInstanceState(...)中看到我的日志消息。这是因为Log.i(tag, msg)中的tag参数太长了。非常感谢您的回复。 - wannik

3

关于 onCreate 问题我不清楚;这些调用来自操作系统。

非静态(“实例”)变量应保存在 instanceState 中;那就是它的作用。参见类似此类帖子,或讨论应用程序生命周期的大部分基本Android教程。


当我旋转模拟器时,onSaveInstanceState(...)没有被调用。而且onPause()不接受任何Bundle。我应该在哪里保存instanceState - wannik
@user639616 你确定吗?应该是这样的,因为活动已被销毁并重新创建。 - Dave Newton
我重写了 onSaveInstanceState(Bundle savedInstanceState) 方法并记录了一些信息。但是我无法看到日志消息。 - wannik
我知道为什么我无法在onSaveInstanceState(...)中看到我的日志消息。那是因为Log.i(tag, msg)中的tag参数太长了。非常感谢您的回复。 - wannik

0
将此行代码放入您的活动中的 AndroidManifest.xml 文件中:
android:launchMode="singleInstance"

来自文档

"singleTask"和"singleInstance"活动只能开始一个任务。 它们始终位于活动堆栈的根部。此外,设备一次只能持有一个活动实例 - 只有一个这样的任务。

另一方面,"singleInstance"活动不允许其他活动成为其任务的一部分。它是任务中唯一的活动。 如果它启动另一个活动,则将该活动分配给不同的任务 - 就像意图中有FLAG_ACTIVITY_NEW_TASK一样。


0
很简单:您可以在代码中重写onConfigurationChanged()方法。
public void onConfigurationChanged(Configuration newConfig) 
    {
        super.onConfigurationChanged(newConfig);

    }

所以现在你的非静态变量不会受到影响,因为此时你的Activity没有被销毁,也没有通过onCreate()方法启动新的Activity。


我是否仍需要在清单文件中设置属性android:configChanges="keyboardHidden|orientation" - wannik
只有在设置了android:configChanges属性时,才会调用onConfigurationChanged方法!这是必须的!!! - Massimo

0
每次旋转屏幕时,活动将被重新创建--这是Android的概念。
但在您的情况下,当活动重新创建时,静态值将被保留,而非静态值将被重新初始化...所以您总是得到n=1...
如果您真的想保留那个非静态值,那么请使用共享首选项。在这里,您可以保留所有需要的值,直到用户清除数据为止...
否则,只需将非静态变量更改为静态变量,这将解决您的问题,我猜。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接