SINGLE_TOP | CLEAR_TOP работает в 95% случаев. Почему 5%?

У меня есть почти готовое приложение с нетривиальной структурой действий. С этим приложением связаны 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 переходит на правильную вкладку, а второй - по умолчанию.


person Cephron    schedule 30.03.2011    source источник


Ответы (1)


TL; DR; Не используйте CLEAR_TOP с SINGLE_TOP одновременно

Если он вызывает ошибку только в 5% случаев, скорее всего, это проблема параллелизма. Вы сказали, что у вас SINGLE_TOP | CLEAR_TOP для вызова Activity B. CLEAR_TOP уничтожает текущий экземпляр Activity B, и намерение доставляется в onCreate (). SINGLE_TOP не уничтожает текущий экземпляр Activity B и передает намерение onNewIntent ().

Когда сначала считывается флаг SINGLE_TOP, намерение доставляется текущему экземпляру Activity B, вызывающему onNewIntent (). Затем читается CLEAR_TOP и действие B уничтожается, и с помощью onCreate () создается новый экземпляр, и все работает нормально.

Когда CLEAR_TOP считывается первым, существующий экземпляр Activity B уничтожается, а новый создается с помощью onCreate (). Затем считывается SINGLE_TOP, и намерение также доставляется в onNewIntent (). Опять же, это работает.

Когда CLEAR_TOP и SINGLE_TOP читаются одновременно, текущий экземпляр активности уничтожается, и CLEAR_TOP вызывает onCreate (), а SINGLE_TOP также вызывает onCreate (), поскольку в настоящий момент не существует экземпляра Activity B. Таким образом, вы получите A-> B-> B.

person siamii    schedule 02.04.2011
comment
Похоже, это помогает. Теперь используя только CLEAR_TOP. Спасибо! - person Cephron; 04.04.2011
comment
Просто хочу отметить: на сегодняшний день в документации явно упоминается , объединяющий ясное верхний и одинарный верхний флаги, так что, по-видимому, сейчас это нормально. - person stkent; 06.12.2016