我有两项活动;让我们说A和B.在activity A
中注册了一个广播接收器,用于监听将完成活动A的特定事件。我在onCreate()
注册广播接收器,并在{{1}中销毁它} onDestroy()
。
为简单起见,activity A
中有一个名为“Destroy Activity A”的button
。当用户点击activity B
时,{@ 1}}应该被销毁。
通常情况下,所有这些都运行顺利,没有任何问题,但问题出现在以下情况中:
1)假设我在button
并且我按Home键将应用程序移动到后台然后如果我使用其他资源繁重的应用程序,Android系统将终止我的应用程序以释放内存。然后,如果我从最近的任务中打开我的应用程序,activity A
将会恢复,并且将调用activity B
,activity B
等方法。现在我按onCreate()
来销毁onResume()
,但活动A已被销毁,因此button
的{{1}},activity A
等方法不会被调用除非我按activity A
转到onCreate()
。因此onResume()
未注册以监听该事件。
2)当用户在设备的设置中从开发者选项中选择“不要保留活动”时,会出现同样的问题。
我一直在寻找解决这个问题很长一段时间,但我找不到合适的答案。处理这种情况的最佳方法是什么?这是Android的错误吗?这个问题应该有一些解决方案。
请帮帮我。
答案 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添加到activitiesToKill
对象。因此,当您单击返回时,将创建活动并且savedInstanceState不为null,活动已完成。
这种方法认为活动E知道它必须杀死哪些活动。
如果您不知道要从E中杀死哪些活动,则必须稍微修改此逻辑:
使用HashMap<String, bool>
创建活动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);
这将完成B
和A
,并将意图传递给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" />
这会产生以下后果:
通常,您不应该看到任何闪烁。
基于这个想法,您可以为您的特定应用构建更好的解决方案。例如,您可以使用FLAG_ACTIVITY_CLEAR_TOP并在活动B的onBackPressed()中完成活动A.