活动创建了两次

8

我已经设置了锁定方向

enter image description here

我已经添加了以下两个简单类的示例代码:

SplashLandscapeActivity.java

public class SplashLandscapeActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("start", "xxxx start Activity SplashLandscapeActivity");
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                startActivity(new Intent(SplashLandscapeActivity.this, TestActivity.class));
                finish();
            }
        }, 500);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d("start", "xxxx onDestroy Activity SplashLandscapeActivity");
    }
}

TestActivity.java

public class TestActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("start", "xxxx start Activity TestActivity "
                + getResources().getConfiguration().orientation);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d("start", "xxxx onDestroy Activity TestActivity "
                + getResources().getConfiguration().orientation);
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".SplashLandscapeActivity"
            android:theme="@style/SplashTheme"
            android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <activity
            android:name=".TestActivity"
            android:screenOrientation="portrait"/>
    </application>
</manifest>

当我使用 new Handler().postDelayedSplashLandscapeActivity.java)来启动 TestActivity 时,它会启动两次。第一次启动时屏幕是横屏,然后又切换回了竖屏。日志显示了全部内容:

xxxx start Activity SplashLandscapeActivity

xxxx start Activity TestActivity 2 // <== landscape

xxxx onDestroy Activity TestActivity 1

xxxx start Activity TestActivity 1 // <== portrait

xxxx onDestroy Activity SplashLandscapeActivity

如果我去掉这个 HandlerTestActivity 就能像正常情况下一样以竖屏方式启动。

xxxx start Activity SplashLandscapeActivity

xxxx start Activity TestActivity 1

xxxx onDestroy Activity SplashLandscapeActivity

所以,我的问题是:
1- 这是系统问题还是预期行为?即使在 Manifest 中将 screenOrientation 设置为固定值,为什么 activity 仍然重新启动?

2- 实际上,我的真正项目没有任何Handler,但是存在相同的问题,即在使用Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK启动后,activity被启动两次。我该如何解决这个问题?


你尝试过修改manifest文件中的屏幕方向设置吗?比如保持两种模式都是竖屏。 - Terril Thomas
如果处理程序被移除并且您在设备处于锁定纵向状态下启动应用程序,那么在到达TestActivity之前应用程序是否会瞬间旋转一下?或者是直接跳转到纵向模式的TestActivity而不进行旋转?我猜测这可能是由于在活动已经执行了onCreateonResume之后调用了config更改,如果没有发生config更改,比如跳过SplashLandscapeActivity直接启动TestActivity,则不会调用它,因此不会重新启动TestActivity - ahasbini
@deadfish,这是基础知识,我希望修复起来像你说的那样容易,但事实并非如此。另外,你能回答我“为什么即使在清单中设置了固定的屏幕方向,活动仍会重新启动”吗? - NamNH
你不必担心为什么Android在旋转时会创建和销毁这些活动,你的活动应该优雅地处理重建。 - gjsalot
@NamNH,你需要修复你的“UI和逻辑代码”。 - gjsalot
显示剩余4条评论
5个回答

4

在您的清单文件中,像这样编辑TestActivity块:

<activity android:name=".TestActivity" android:launchMode="singleInstance" android:screenOrientation="portrait"/>


(Note: 该段内容已经是中文)

2

有2个评论可以防止TestActivity启动两次。希望能帮到你

  1. 在TestActivity中使用sensorPortrait代替portrait,这样TestActivity就不会启动两次,但它会旋转以匹配用户持有设备的方式。
  2. 在Manifest.xml文件中添加android:configChanges="keyboardHidden|orientation|screenSize"到TestAcitivty中。它将调用public void onConfigurationChanged(Configuration newConfig)而不是重新启动。

我在Android N中没有发现这个问题。


如果LandPort之间的UI不同,那么在onConfigurationChanged中使用setContentView是否推荐?此外,您能回答我为什么即使在清单中将screenOrientation设置为固定值,活动仍会重新启动的问题吗? - NamNH
我还没有找到任何官方文件说明为什么当屏幕方向与启动器不同时会重新启动Activity。但是,似乎Android会根据启动器的方向启动Activity,并忽略当前的横屏状态。这个原因似乎是为了预测你正在使用的状态。因此,您必须在运行时处理此问题。链接 - VictorPanda
不确定starter或任何关系,因为没有handleractivity会按照完全相同的manifest配置启动。无论如何,感谢您的时间,这个问题的主要思想不是关于“如何修复...”,而是关于“为什么...”。 - NamNH

1

更新你的 manifest.xml

 <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example">

        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity
                android:name=".SplashLandscapeActivity"
                android:theme="@style/SplashTheme"
                android:screenOrientation="landscape"
            android:configChanges="keyboardHidden|orientation|screenSize">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN"/>

                    <category android:name="android.intent.category.LAUNCHER"/>
                </intent-filter>
            </activity>

            <activity
                android:name=".TestActivity"
                android:screenOrientation="portrait"
android:configChanges="keyboardHidden|orientation|screenSize"/>
        </application>
    </manifest>

我不喜欢configChanges选项,而且你的回答并没有针对我的问题。 - NamNH
如果您不想使用configChanges,则应该使用以下代码:<activity android:name=".TestActivity" android:launchMode="singleInstance" android:taskAffinity="">更多详情请参考:https://inthecheesefactory.com/blog/understand-android-activity-launchmode/en - Aniruddh Parihar

1
我认为你需要添加标志 </p>
android:configChanges="orientation|screenSize"

将清单文件中的TestActivity更改为以下内容:
<activity
android:name=".TestActivity"
android:screenOrientation="portrait"
android:configChanges="orientation|screenSize"
/>

这应该可以解决您的问题。

需要更改配置的原因是: 现在关于为什么需要更改configChanges,来自Android文档:

如果您的应用程序不需要在特定配置更改期间更新资源,并且您有性能限制要求您避免活动重新启动,则可以声明您的活动自己处理配置更改,从而防止系统重新启动您的活动。

请访问android文档


你的回答没有解决我的问题。请查看我在这里的评论(https://stackoverflow.com/questions/44104109/activity-created-twice#comments-44213942)。 - NamNH
你试过了吗?我用我建议的更改得到了以下日志: D/start: xxxx 启动 Activity TestActivity 1 D/start: xxxx 销毁 Activity SplashLandscapeActivity - Rahul Shukla
和你之前的评论一样,你的答案没有关注我的问题。我已经设置了固定的“screenOrientation”,为什么还需要设置“configChanges”选项呢? - NamNH
但它是否解决了问题?这是首要关注的问题。请查看我的更新答案。 - Rahul Shukla
不,用你的简单代码修复我的真实项目并不容易,它会引起许多副作用。我想在这里提出我的问题,以找出我的问题根源:“为什么我需要设置configChanges选项,而我已经设置了固定的screenOrientation?” - NamNH
根据你的样例代码,有两个活动分别在横屏和竖屏中。Android 将按照清单声明来执行方向。除非你想自己处理配置更改,否则从一个方向切换到另一个方向将会重新创建你的活动。如需了解更多信息,请访问我更新答案中指定的 Android 文档链接。希望这能帮到你! 你在谈论哪些副作用?也许你需要重新审视一下你的项目编写方式? - Rahul Shukla

0

可以将MainActivity更改为

public class SplashLandscapeActivity extends AppCompatActivity {
    static boolean isRunStop = false;
    static int counter = 0;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
            counter++;
            Log.d("start", "xxxx run handler SplashLandscapeActivity. Time : "+counter);
            Toast.makeText(SplashLandscapeActivity.this, ""+counter, Toast.LENGTH_SHORT).show();
            new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.d("start", "xxxx run handler SplashLandscapeActivity");
                if(!isRunStop )
                {
                      startActivity(new Intent(SplashLandscapeActivity.this, TestActivity.class));
                      isRunStop =true;
                      finish();
                }

            }
        }, 500);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d("start", "xxxx onDestroy Activity SplashLandscapeActivity");
    }
}

当您更改屏幕方向时,处理程序在另一个线程中被调用,该线程不会消失,之前的处理程序已经停止并运行。您可以有更多时间测试处理程序,以便在更改屏幕方向时进行多次测试。


你的回答没有针对我的问题。而且,正如我所说的,startActivity 只被调用了一次,但是 TestActivity 却启动了两次。 - NamNH
记录 Log.d("start", "xxxx run handler SplashLandscapeActivity"); 这段代码运行了两次。 - Ahmad Aghazadeh
你的模拟器版本是哪个?在我所有的模拟器上,Handler只运行一次。 - NamNH
我的模拟器版本是23。为了测试,将时间从500增加到5000,并快速更改屏幕方向。 - Ahmad Aghazadeh
谢谢你的时间,但我不希望你尝试创建新的案例。 - NamNH

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