【问题标题】:A finished Activity re-created after a coincident orientation change在一致的方向更改后重新创建已完成的活动
【发布时间】:2013-03-26 21:10:52
【问题描述】:

我不知道这种行为是否正常,因为我认为调用 finish() 会阻止重新创建活动。

这里是活动类的摘录:

public class MyActivity extends FragmentActivity {

    private RetainFragment retainFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        ......

        retainFragment = getSupportFragmentManager().findFragmentByTag("RetainFragment");
        if(retainFragment == null) {
            retainFragment = new RetainFragment();
            getSupportFragmentManager().beginTransaction().add(retainFragment, "RetainFragment").commitAllowingStateLoss();
        }
        if(retainFragment.isFinish) {
            Log.v("MyActivity", "isFinish == true");
            this.finish();
        }
    }

    // a on-click event handler for a finish button
    public void onFinishClicked(View view) {
         retainFragment.isFinish = true;
         this.finish();
    }

    private class RetainFragment extends Fragment {
        private boolean isFinish = false;

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            this.setRetainInstance(true);
        }
    }

}

活动是持久的(我也将其他持久变量存储在 RetainFragment 中,但我没有显示它们)并且仅在用户单击完成按钮后关闭,此时将调用 onFinishClicked(View),该活动应由系统完成。我预计它不会在屏幕同时旋转后被系统重新创建。这种情况很少发生,我通过在 onCreate() 方法中再次调用 finish() 来处理它。但是,这里看起来很丑,因为完成的活动应该永远死掉,现在我必须明确地处理它。 :( 我对活动生命周期或保留片段是否有任何误解?

【问题讨论】:

    标签: android orientation restart oncreate activity-finish


    【解决方案1】:

    如果您在 Android 上工作,每当您听到自己说“此 Activity 将仅在 X 之后关闭”时。不。你永远不应该依赖具有高度控制生命周期的活动。您的 Activity 的设计方式应该是,无论何时销毁和重新创建它,它都能正常工作。

    要跨轮换等保留信息,请将代码添加到 onSaveInstanceBundle,然后在您的 onCreate 上检查它并提取保存的值。

    【讨论】:

      【解决方案2】:

      这是标准的 Android 行为。看看这段Androiddocumentation

      除非您另外指定,否则配置更改(例如屏幕方向、语言、输入设备等的更改)将导致您当前的活动被破坏,并通过 onPause() 的正常活动生命周期过程,onStop( ) 和 onDestroy() 视情况而定。如果 Activity 位于前台或用户可见,在该实例中调用 onDestroy() 后,将创建该 Activity 的新实例,使用前一个实例从中生成的 savedInstanceState onSaveInstanceState(Bundle)。

      为避免这种情况,您可以在其清单中设置 android:configChanges 属性。对于您说在那里处理的任何类型的配置更改,您将收到对当前活动的 onConfigurationChanged(Configuration) 方法的调用,而不是重新启动。但是,如果配置更改涉及您未处理的任何内容,则该活动仍将重新启动,并且不会调用 onConfigurationChanged(Configuration)。

      【讨论】:

      • 在调用finish()之后,activity应该会经历onPause()、onStop()、onDestroyed()的生命周期,所以在一小段时间之后,它应该不再可见用户和系统不应启动该活动的另一个实例。我认为这是在那个小时间窗口内发生的配置更改导致了这种奇怪的行为。处理配置更改给程序增加了太多的复杂性而收效甚微,难怪许多应用程序开发人员选择修复屏幕方向。我想我会试试你的方法。感谢您的回答。
      • 我遇到了和 Ronald 一样的问题,但我们观察到的特定行为是在方向更改时重新创建完成()的 Activity,即使 Activity 的方向指定为通过清单锁定。例如,我有一个 Activity A,它使用 startActivityForResult 启动 Activity B。 B 的方向被锁定。如果我将设备的方向从纵向更改为横向,然后在 B 上运行调用 finish() 的操作(例如通过按钮),则 B 将完成,但调用的是 B 的 onActivityResult 方法而不是 A!
      • 我找出了在我的特定情况下导致错误的原因。事实证明这是一个应用程序错误,我正在共享用于启动活动的 Intent 对象。 I posted my findings and solution as an answer.
      【解决方案3】:

      我遇到了同样的问题。也就是说,我有一个 Activity A,它将使用 startActivityForResult 启动一个 Activity B。 B 的方向被清单锁定。 A的方向没有锁定。

      当我旋转我的设备以使方向不同时,将不会重新创建活动 B。这是预期和正常的。然而,一旦我执行了一个将完成()活动 B 的 UI 操作,而不是看到活动 A 被重建并显示在屏幕上,我再次看到活动 B。此外,调用的是 Activity B 的 onActivityResult 而不是 A 的 onActivityResult。

      我知道发生了什么。我使用用于启动 A 的相同 Intent 对象也启动 B。在方向更改和 B 完成后,Android 系统检测到 Activity A 需要重新构建,因为 A 是在方向不同时最后构建的。系统将使用最初用于构建 A 的相同 Intent 对象重构 A。但是,Intent 对象被修改为启动 B,因此系统将重构 Activity B 而不是 Activity A。

      我的代码遵循类似于以下的逻辑:

      public class A extends Activity {
           @Override
           protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              //...omitted code for clarity...
              Intent i = getIntent();
              i.setClass(this, B.class);
              startActivityForResult(i, 0);
           }
      
           @Override
           public void onActivityResult(int requestCode, int resultCode, Intent data) {
               //...omitted code for clarity...
           }
       }
      

      我的活动 B 看起来像:

      public class B extends Activity {
           //...omitted code for clarity....
      
           public void myFinishingClickHandler(View clickedView) {
               //...omitted code for clarity...
               finish();
           }
      }
      

      场景快速演练:

      1. 设备处于纵向模式。用户启动 Activity A。系统将构造带有 Intent I 的 Activity A,其目标设置为 A。
      2. 活动 A 通过将目标更改为 B 来修改其 Intent I。
      3. Activity A 以 Intent I 启动一个 Activity。系统将构造 Activity B,因为 I 现在指向 B。
      4. B 现在正在运行。用户将设备方向更改为横向。
      5. 用户单击单击侦听器调用 myFinishingClickHandler 的按钮。该方法在 Activity B 上调用 finish()。
      6. 活动 B 结束。系统开始恢复 Activity A 的过程。
      7. 系统指出,Activity A 是在设备处于纵向时构建的,但现在设备处于横向模式。系统将尝试重新创建活动 A。现有的 Activity A 通过其销毁流程(调用 onDestroy)发送。
      8. 系统将使用保存的 Intent I 重构 Activity A。但是,I 的目标仍然是 Activity B。系统改为构建 Activity B 而不是 Activity A。

      解决方案:不要分享 Intent 来启动活动。使用 Intent 复制构造函数。

      另外,锁定 Activity 的方向以减少对方向变化的担心(其他配置更改会导致重新构建您的 Activity,例如更改语言)。

      【讨论】:

        猜你喜欢
        • 2010-11-15
        • 1970-01-01
        • 2023-03-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-03-07
        • 1970-01-01
        • 2015-03-25
        相关资源
        最近更新 更多