当活动被破坏时,活动背叛的不当行为

时间:2015-04-10 12:02:15

标签: android android-activity broadcastreceiver android-lifecycle back-stack

我有两项活动;让我们说A和B.在activity A中注册了一个广播接收器,用于监听将完成活动A的特定事件。我在onCreate()注册广播接收器,并在{{1}中销毁它} onDestroy()

为简单起见,activity A中有一个名为“Destroy Activity A”的button。当用户点击activity B时,{@ 1}}应该被销毁。

通常情况下,所有这些都运行顺利,没有任何问题,但问题出现在以下情况中:

1)假设我在button并且我按Home键将应用程序移动到后台然后如果我使用其他资源繁重的应用程序,Android系统将终止我的应用程序以释放内存。然后,如果我从最近的任务中打开我的应用程序,activity A将会恢复,并且将调用activity Bactivity B等方法。现在我按onCreate()来销毁onResume(),但活动A已被销毁,因此button的{​​{1}},activity A等方法不会被调用除非我按activity A转到onCreate()。因此onResume()未注册以监听该事件。

2)当用户在设备的设置中从开发者选项中选择“不要保留活动”时,会出现同样的问题。

我一直在寻找解决这个问题很长一段时间,但我找不到合适的答案。处理这种情况的最佳方法是什么?这是Android的错误吗?这个问题应该有一些解决方案。

请帮帮我。

7 个答案:

答案 0 :(得分:6)

  

如果您的Activity A已被Android操作系统本身销毁,那么就有了   无法追踪。

有些人建议通过在Activity A方法中列出事件来跟踪onDestroy但是如果您的Activity被系统操作系统杀死,那么请注意这里不会调用这些方法。

答案 1 :(得分:3)

在保持当前广播逻辑的同时无法解决此问题。

从后台堆栈中杀死活动,imo,并不是一种正确的方法。您应该强烈考虑更改导航的逻辑。

但是如果你的项目很大并且时间问题,那么重构是不可能的,A.J。方法有效,但你提到你有很多活动需要被杀死,他的解决方案变得非常棘手。

我建议如下。这可能不是最好的主意,但我想不出另一个。所以也许这可能有所帮助。

您应该拥有以下内容:

  • 您所有活动的基本活动。
  • 应用程序级别的ArrayList<String> activitiesToKill对象。 (如果你没有扩展Application,你可以将它作为静态变量

首先,我们必须确保当操作系统在低内存中杀死应用程序时activitiesToKill不会丢失。在BaseActivity我们在onSaveInstanceState期间保存列表并将其恢复为onRestoreInstanceState

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putSerializable("activitiesToKill", activitiesToKill);
}

private void onRestoreInstanceState(Bundle state) {
    if (state != null) {
        activitiesToKill = (ArrayList<String>) state.getSerializable("activitiesToKill");
    super.onRestoreInstanceState(state); 
}

}

这里的想法是通过使用他们的名字来保存列表中应该杀死的活动。

逻辑如下:

假设您有活动A,B,C,D和E

从活动E,你按下按钮,你想要杀死B和D

按E中的按钮时,将B和D的名称添加到activitiesToKill对象。

activitiesToKill.add(B.class.getSimpleName()
activitiesToKill.add(D.class.getSimpleName()

在BaseActivity的onCreate方法中,我们必须检查是否

if(savedInstanceState != null)
{
    //The activity is being restored. We check if the it is in the lest to Kill and we finish it                
    if(activitiesToKill.contains(this.getClass().getSimpleName()))
    {
        activitiesToKill.remove(this.getClass().getSimpleName())
        finish();
    }
}

如果通过广播将其杀死,请务必删除该活动的名称。

所以基本上这就是每种情况下都会发生的事情。

如果应用程序正常运行,并且您单击按钮,广播将被发送,B和D将被杀死。务必从activitiesToKill

中删除B和D.

如果应用程序被杀死并恢复,您按下按钮,广播将无效,但您已将B和D添加到activitiesToKill对象。因此,当您单击返回时,将创建活动并且savedInstanceState不为null,活动已完成。

这种方法认为活动E知道它必须杀死哪些活动。

如果您不知道要从E中杀死哪些活动,则必须稍微修改此逻辑:

使用HashMap<String, bool>

而不是使用ArrayList

创建活动B时,它会将自己注册到hashmap:

activitiesToKill.put(this.class.getSimpleName(), false)

然后从活动E,您所要做的就是将所有条目设置为true

然后在基础活动的创建中你必须检查这个活动是否在activitiesToKill中注册(hashmap包含键)和boolean是true你杀了它(不要忘记返回它是假的,或删除密钥)

这确保每个活动向HashMap注册自己并且活动E没有知道所有要杀死的活动。并且不要忘记删除它们以防广播杀死它们。

这种方法还确保在从intent中正常打开时不会杀死活动,因为在那种情况下onSaveInstanceState在onCreate中将为null,因此不会发生任何事情。

如果您有需要通过不同条件终止的活动组(不仅是按钮单击),则可以完成更高级的检查,这样您就可以使用HashMap的HashMap将它们分类。

另请注意,如果您有多个具有相同名称但不同包的活动,则可以使用getName而不是getSimpleName。

我希望我的解释清楚,因为我是从头脑中写下来的,如果有任何方面不清楚,请告诉我。

祝你好运

答案 2 :(得分:0)

我不知道是否有可能在&#34;正确的&#34;方式。

我想到的是以某种方式标记A活动。您无法使用startActivityForResult(),因为您会在调用onResume()之前收到结果,即用户界面已被夸大。

如果您使用的是Otto,则可以尝试使用粘性事件。否则,您将需要一个单例来处理该标志或将其保存到共享首选项。

在调用onCreate()之前,您必须在setContentView()方法上检查该标记,如果该标志为true则完成活动。

答案 3 :(得分:0)

根据您提供的信息,您在检查其是否已注册后,如何在活动B的onCreate中注册广播。如果在您提到的任一场景中调用了活动A的onDestroy,那么将调用广播的注销。因此,在这种情况下,您可以在活动B的onCreate中注册您的广播,这样即使您的背书中只有活动B,您也可以收听它。

答案 4 :(得分:0)

您是否考虑过使用Sticky Broadcast? 此外,您可以在应用程序级别(在清单中)注册您的接收器,并在不考虑Activity A状态的情况下收听此事件。

但是,就像已经说过 Youssef 一样,从后台进行的杀戮活动并不是一种正确的做法。您应该强烈考虑更改导航的逻辑。

答案 5 :(得分:0)

Activities的主要规则之一是您不能依赖除前景活动之外的任何活动。您尝试使用广播进行的操作与后台堆栈无关 - 后台堆栈并不能保证所有活动始终处于活动状态,但它会确保它们在重新启动时重新创建。 #39;是时候走向前景了。

在您的示例中(如果我了解您的目标是什么),您需要导航到A下面的某些内容 - 例如,活动Z,并且堆栈看起来像这样:Z-A-[B]。正常的事件过程中,您点击了back,然后是A,然后是另一次点击 - Z,但在某种情况下(比如按一个按钮) )你想要回到Z绕过A - 这是使用FLAG_ACTIVITY_CLEAR_TOP并明确启动Z 的经典案例:

Intent intent = new Intent(this, ActivityZ.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

这将完成BA,并将意图传递给Z。你可能还需要FLAG_ACTIVITY_SINGLE_TOP标志,密切注意FLAG_ACTIVITY_CLEAR_TOP的描述,你应该考虑一些诡计。

答案 6 :(得分:-1)

我想到了很多解决方案,但由于您没有提供有关您的应用的大量信息,所以我认为这应该可以正常使用。

在活动B中按下“杀死活动A”按钮时,只需执行以下代码,而不是触发广播以杀死活动A.

        Intent intent = new Intent(getApplicationContext(),
                ActivityA.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
        intent.putExtra("EXIT", true);
        startActivity(intent);

在活动A中添加以下代码

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    if (intent.getBooleanExtra("EXIT", false)) {
        finish();
    }
}

protected void onCreate(Bundle savedInstanceState) {
    //Ideally, there should not be anything before this
    super.onCreate(savedInstanceState);
    if(getIntent().getBooleanExtra("EXIT", false)){
        finish();
        return;
    }

在活动A的清单集“singleTop”启动模式中。

<activity
    android:name=".ActivityA"
    ...
    android:launchMode="singleTop" />

这会产生以下后果:

  • 如果活动A已经在运行,它将被带到活动堆栈的前面并完成,从而将其从堆栈中移除。
  • 如果活动A已被销毁但仍然存在于活动堆栈中(按下后退按钮时将启动),它将被启动,结束并完成,从而将其从活动堆栈中移除。
  • 如果活动A已经被销毁并且不存在于活动堆栈中,并且您仍然按下“删除活动A”按钮,它将被启动,被带到前面并完成。

通常,您不应该看到任何闪烁。

基于这个想法,您可以为您的特定应用构建更好的解决方案。例如,您可以使用FLAG_ACTIVITY_CLEAR_TOP并在活动B的onBackPressed()中完成活动A.