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

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

为简单起见, activity B有一个名为“销毁活动A”的button 。 当用户点击button ,应销毁activity A

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

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

2)当用户在设备的设置中从Developer选项中选择“Do not keep activities”时,会出现同样的问题。

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

请帮帮我。

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

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

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

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

你应该有以下几点:

  • 适用于所有活动的基本活动。
  • 应用程序级别的ArrayList activitiesToKill对象。 (如果没有扩展Application ,可以将它作为静态variables

首先,我们必须确保当操作系统在低内存中杀死应用程序时,eventsToKill不会丢失。 在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) 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中杀死哪些活动,您必须稍微修改此逻辑:

而不是使用ArrayList使用HashMap

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

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

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

然后在创建基本活动时,你必须检查这个活动是否在activitiesToKill中注册(hashmap包含键)并且boolean为true你杀了它(不要忘记将它返回false,或者删除键)

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

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

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

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

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

祝你好运

如果您的Activity A已被Android操作系统本身销毁,则无法跟踪。

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

我不知道是否有可能以“正确”的方式处理这个问题。

我想到的是以某种方式标记A活动。 你不能使用startActivityForResult()因为你会在调用onResume()之前收到结果,即UI已经膨胀了。

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

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

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

你考虑过使用Sticky Broadcast吗? 此外,您可以在应用程序级别(在清单中)注册您的接收器,并监听此事件,无论Activity A状态如何。

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

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

在你的例子中(如果我理解你的目标是什么)你需要导航到A下面A东西 – 比如,活动Z ,并且堆栈看起来像这样: ZA-[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的描述,您应该考虑一些技巧。

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

而不是触发广播来杀死活动A,只需在活动B中按下“杀死活动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”启动模式中。

  

这将产生以下后果:

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

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

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