SINGLE_TOP | CLEAR_TOP似乎在95%的时间都有效。为什么5%?

时间:2011-03-30 17:04:51

标签: android android-activity android-intent launchmode

我有一个近乎完成的应用程序,具有非平凡的活动结构。有与此应用程序关联的推送通知,无论应用程序是前台/后台/未激活,选择通知条目都应该启动特定活动。

如果应用未激活,我已成功启动应用并自动导航到相应的部分。但是,当应用程序处于活动状态时,我遇到了问题。我将提出问题的简化版本,以传达问题的本质,我将根据需要发布我的应用程序的活动结构和相关代码的详细信息(实际上,现在正在处理)。

所以,我的应用程序的活动堆栈(大大简化)看起来像这样:

A - > B - > X

其中A,根活动,是登录页面; B是“主页”,X是可以从主页启动的几个活动之一(但一次只有一个活动;因为这些只能从B开始)。

当选择通知时,我需要应用程序自动导航到B,无论事先处于什么状态 - 是否[A],[A - > B],[A - > B - > X]或[](app未激活)。

我的通知将Intent传递给活动A.我尝试过使用CLEAR_TOP和NEW_TASK标志,但没有。目前有launchmode = singleTask。这样做,我想我正在解决所有可能的现有堆栈配置并将它们减少到[A]。 Intent还带有一个额外的东西,它将其标识为来自通知,而不是通常的发布。

活动A,在识别出从通知发送的Intent(它可以在onCreate()和onNewIntent()中执行此操作)时,将Intent发送到Activity B.此Intent包含CLEAR_TOP和SINGLE_TOP。 B有launchmode = singleTop。

95%的时间,这可以按照需要工作,按下通知后,应用程序的堆栈是[A - > B]。 大约5%的时间,应用程序不知何故最终得到一堆[A - > B - > B]。

关于这里发生了什么,或者我做错了什么的任何想法?

如果事实证明这是一个非常重要的问题,我会发布更多细节。事实上,现在发布更多细节......

~~~~~~~~~~~~~~~~~~~~~~~更多详情~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~

单步执行调试器会显示,每次A将其意图发送给B时,现有的B实例都是onDestroy(),然后才能进入onCreate(),然后调用onNewIntent()。这对我来说似乎很奇怪,并且暗示我误解了我正在使用的标志(CLEAR_TOP和SINGLE_TOP),或者其他东西正在干扰它们。

我没有成功地在调试中重现错误的堆栈结构。不确定是不是因为它没有在调试中发生,或者我没有尝试过足够的时间。

正在进行的意图代码:

在C2DM接收器服务中:

protected void onMessage(Context context, Intent intent) {
    int icon = R.drawable.some_drawable;
    CharSequence tickerText = "blah";
    long when = System.currentTimeMillis();
    Notification notification = new Notification(icon, tickerText, when);

    //Context context = getApplicationContext(); //Don't need this; using the context passed by the message.
    CharSequence contentTitle = intent.getStringExtra("payload");
    CharSequence contentText = "Lorem ipsum dolor si amet,";
    Intent notificationIntent = new Intent(this, LoginPage.class);
    //notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); //Tried with and without
    notificationIntent.putExtra(PushManager.PUSH_INTENT, PushManager.PUSH_INTENT); //Indicator that this was send from notification

    PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
    notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);

    notificationManager.notify(PushManager.ALARM_NOTIFICATION_ID, notification);
}

在LoginPage(活动A)中,成功登录后:

Intent i = new Intent(LoginPage.this, TabHomePage.class);
// (If we're automatically going to tab 2, inform next activity)
if(fromNotification) {
    i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
    i.putExtra(TabHomePage.TAB_NUMBER, TabHomePage.TAB_2);
}
startActivity(i);

有关活动堆栈结构的更多详细信息,请参见图片:

http://i89.photobucket.com/albums/k207/cephron/ActivityStack.png

这是千言万语:

活动A是登录页面;成功登录后,它启动B. B是TabActivity,其中包含三个活动(由C,D,E表示)。 C,D和E中的每一个实际上都是一个ActivityGroup,其子活动模仿活动的通常堆栈行为。

因此,每个选项卡都包含自己的活动堆栈,并且选项卡之间的切换会更改当前正由用户导航推送/弹出哪些堆栈(每个选项卡包含一堆ListActivities浏览实体的层次结构) )。这些也可以在巨大的“B”TabActivity之外开始新的活动(由X表示)。

因此,从活动A登录后,在接受更多用户输入之前,至少会创建三个活动: 创建-B(并且可以看到,因为TabWidget现在位于屏幕顶部) - 创建一个ActivityGroup:无论哪个属于默认选项卡。此活动组在屏幕上仍未显示,但是......它仅显示其子活动堆栈的顶级活动。 - 所以,最后,创建了ActivityGroup堆栈的“根”活动(在图中,F是这样一个Activity的一个例子)。此活动显示在TabWidget下方。

每次访问每个选项卡一次后,不再通过在选项卡之间切换(不计算内存终止)来创建/销毁活动。 在任何选项卡中按回()后面的堆栈顶部的活动,显示其下方的活动。 在任何选项卡中从根活动(如F)返回完成整个TabActivity,将用户发送回A。

传递给B的意图还指示它自动导航到与默认选项卡不同的选项卡。在我们最终得到一堆[A - >的情况下B - > B],第一个B导航到正确的选项卡,第二个B是默认选项卡。

1 个答案:

答案 0 :(得分:14)

<强> TL; DR;不要同时将CLEAR_TOP与SINGLE_TOP一起使用

如果它只在5%的时间内产生错误,则可能是并发问题。你说你有SINGLE_TOP | CLEAR_TOP用于调用Activity B. CLEAR_TOP销毁活动B的当前实例,并将意图传递给onCreate()。 SINGLE_TOP不会销毁活动B的当前实例,并将意图传递给onNewIntent()。

当首先读取SINGLE_TOP标志时,意图被传递给调用onNewIntent()的活动B的当前实例。然后读取CLEAR_TOP并销毁活动B并使用onCreate()创建一个新实例,一切正常。

首先读取CLEAR_TOP时,将销毁活动B的现有实例,并使用onCreate()创建一个新实例。然后读取SINGLE_TOP并将意图传递给onNewIntent()。它再次成功。

当同时读取CLEAR_TOP和SINGLE_TOP时,活动的当前实例将被销毁,CLEAR_TOP也会调用onCreate()和SINGLE_TOP调用onCreate(),因为此时不存在活动B的实例。因此,您最终得到A-> B-> B。