У меня есть почти готовое приложение с нетривиальной структурой действий. С этим приложением связаны push-уведомления, и выбор записи уведомления должен вызывать конкретное действие независимо от того, является ли приложение передним / фоновым / неактивным.
Если приложение неактивно, я смог успешно запустить приложение и автоматически перейти к соответствующей части. Однако, когда приложение активно, у меня возникает проблема. Я представлю упрощенную версию проблемы, чтобы сообщить о характере проблемы, и я опубликую подробную информацию о структуре активности моего приложения и соответствующем коде по мере необходимости (фактически, работаю над этим сейчас).
Итак, стек активности моего приложения (сильно упрощенный) выглядит так:
A -> B -> X
Где A, корневая активность, является страницей входа; B - это что-то вроде «домашней страницы», а X - одно из нескольких действий, которые можно запустить с домашней страницы (но одновременно активен только один экземпляр; они могут быть запущены только с B).
Когда уведомление выбрано, мне нужно, чтобы приложение автоматически переходило к B, независимо от того, в каком состоянии оно находилось заранее - будь то [A], [A -> B], [A -> B -> X] или [] ( приложение не активно).
Мое уведомление передает намерение действию A. Я пробовал использовать флаги CLEAR_TOP и NEW_TASK, но ничего. A в настоящее время имеет launchmode = singleTask. Делая это, я полагаю, что обращаюсь ко всем возможным существующим конфигурациям стека и сокращаю их до [A]. Intent также содержит дополнительную информацию, которая идентифицирует его как поступившее из уведомления, в отличие от обычного запуска.
Действие A, идентифицировав намерение как отправленное из уведомления (оно может делать это как в onCreate (), так и в onNewIntent ()), отправляет намерение в действие B. Это намерение содержит CLEAR_TOP и SINGLE_TOP. B имеет launchmode = singleTop.
В 95% случаев это работает должным образом, и после нажатия на уведомление стек приложения становится [A -> B]. Примерно в 5% случаев приложение каким-то образом получает стек из [A -> B -> B].
Есть идеи о том, что здесь происходит, или что-то я делаю не так?
Я опубликую более подробную информацию, если это окажется нетривиальной проблемой. Фактически, сейчас размещаю более подробную информацию ...
~~~~~~~~~~~~~~~~~~~~~~ БОЛЬШЕ ДЕТАЛИ ~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
Пошаговое выполнение отладчика показывает, что каждый раз, когда A отправляет свое намерение B, существующий экземпляр B является onDestroy () 'd перед тем, как он будет onCreate ()' d, а затем также будет вызван его 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 (Activity 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, который содержит в себе три Activity (представленные C, D, E). Каждый из C, D и E на самом деле является ActivityGroup, дочерние Activity которой имитируют обычное поведение стека для Activity.
Таким образом, каждая вкладка содержит свой собственный стек действий, и переключение между вкладками меняет, какой из этих стеков в настоящее время выталкивается / выталкивается навигацией пользователя (каждая вкладка содержит стек ListActivities, просматривающих иерархическую структуру сущностей). Они также могут запускать новые действия, выходящие за рамки гигантской TabActivity «B» (обозначенной X).
Итак, после входа в систему из Activity A, по крайней мере, три действия создаются до того, как будет принят дополнительный ввод пользователя: -B создается (и отображается, потому что TabWidget теперь находится в верхней части экрана) -One из ActivityGroups создается : тот, который принадлежит вкладке по умолчанию. Эта ActivityGroup остается непредставленной на экране, однако ... она показывает только верхнюю активность своего стека дочерних действий. -Итак, наконец, создается «корневая» активность стека этой ActivityGroup (на рисунке F является примером такого Activity). Это действие отображается под TabWidget.
После того, как каждая вкладка была посещена один раз, больше не создаются / уничтожаются действия при переключении между вкладками (не считая убийств памяти). Нажатие назад в любой вкладке завершает () действие наверху этого стека, показывая то, что находится под ним. Нажатие назад из корневого действия (например, F) на любой вкладке завершает всю TabActivity, отправляя пользователя обратно в A.
Намерение, переданное B, также указывает ему автоматически перейти на вкладку, отличную от вкладки по умолчанию. В случае, когда мы получаем стек из [A -> B -> B], первый B переходит на правильную вкладку, а второй - по умолчанию.