Cep*_*ron 14 android android-intent launchmode android-activity
我有一个几乎完成的应用程序与非平凡的活动结构.有与此应用程序关联的推送通知,并且无论应用程序是前台/后台/未激活,选择通知条目都应该启动特定活动.
如果应用程序未处于活动状态,我可以成功启动应用程序并自动导航到相应的部分.但是,当应用程序处于活动状态时,我遇到了问题.我将提出问题的简化版本,以传达问题的本质,我将根据需要发布我的应用程序的活动结构和相关代码的详细信息(实际上,现在正在处理).
所以,我的应用程序的活动堆栈(大大简化)看起来像这样:
A - > B - > X.
其中A,根活动,是一个登录页面; B是"主页",X是可以从主页启动的几个活动之一(但一次只有一个活动;因为这些只能从B开始).
当选择通知时,我需要应用程序自动导航到B,无论事先处于什么状态 - 无论是[A],[A - > B],[A - > B - > X]还是[](应用程序未激活).
我的通知将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);
}
Run Code Online (Sandbox Code Playgroud)
在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);
Run Code Online (Sandbox Code Playgroud)
有关活动堆栈结构的更多详细信息,请参见图片:
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现在位于屏幕顶部) - 创建了一个ActivityGroups :无论哪个属于默认选项卡.此活动组在屏幕上仍未显示,但是......它仅显示其子活动堆栈的顶级活动. - 所以,最后,创建了ActivityGroup堆栈的"根"活动(在图中,F是这样一个Activity的一个例子).此活动显示在TabWidget下方.
在访问过一次每个选项卡后,不再通过在选项卡之间切换(不计算内存终止)来创建/销毁活动.在任何选项卡中按回()后面的堆栈顶部的活动,显示其下方的活动.在任何选项卡中从根活动(如F)返回完成整个TabActivity,将用户发送回A.
传递给B的意图还指示它自动导航到与默认选项卡不同的选项卡.在我们最终得到[A - > B - > B]堆栈的情况下,第一个B导航到正确的选项卡,第二个B是默认选项卡.
sia*_*mii 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.