Восстановить задний стек Android после закрытия приложения

Как лучше всего управлять / восстанавливать бэкстек приложения между несколькими сеансами?

Пример рабочего процесса:

  1. Активность A запущена (стек: A)
  2. Активность B запущена (стек: A B)
  3. Активность C запущена (стек: A B C)
  4. ...
  5. Пользователь какое-то время использует другое приложение (скажем, приложение GMail)
  6. ...
  7. Пользователь возвращается к моему приложению, но Android очищает задний стек.

На шаге 7 я хотел бы возобновить действие C, и если пользователь нажмет кнопку «Назад» 2 раза, он вернется к действию B, а затем к действию A.

[Изменить] Добавление деталей.

После шага 7 выше, по умолчанию в Android происходит следующее:

  1. Активность A запущена (стек: пуст, добавлен C)

И я хотел бы, чтобы пользователь чувствовал, что он все еще использует тот же сеанс:

  1. Действие C возобновлено (стек: A B C)
  2. Пользователь нажимает кнопку «Назад», действие B возобновляется (стек: A B)
  3. Пользователь нажимает кнопку «Назад», действие A возобновляется (стек: A)

Что было бы хорошим подходом к этой ситуации, избегая утечек памяти?

[Второе EDIT] Я разрабатывал обходной путь, используя общий класс UIController для всех действий и LauncherActivity для делегирования логики UIController.

Поскольку мне нужно перестроить задний стек только при запуске ActivityC, это решение работает нормально:

public class UIController
{
    private boolean _launched = false;

    static private final UIController __instance = new UIController();
    static public UIController getInstance() { return __instance; }

    // Enforces the Singleton Pattern by preventing external access to constructor
    private UIController() { }

    public void onActivityCreated(Activity activity) {
        if (!_launched)
        {
            if ( shouldRebuildStack() )
            {
                // Rebuild Activity stack

                // Npte : actually Android will add ActivityA and ActivityB to the stack
                // but will *NOT* create them right away. Only ActivityC will be 
                // created and resumed.
                // Since they are in the back stack, the other activities will be 
                // created by Android once needed.
                startActivity(activity, ActivityA.class);
                startActivity(activity, ActivityB.class);
                startActivity(activity, ActivityC.class);
            } else {
                // Starts default activity
                startActivity(activity, ActivityA.class);
            }

            _launched = true;
        }
    }

    public void onActivityResumed(Activity activity) {
        memorizeCurrentActivity( activity.getClass().toString() );
    }

    private void memorizeCurrentActivity( String className ) {
        // write className to preferences, disk, etc.
    }

    private boolean shouldRebuildStack() {
        String previousActivity = " [load info from file, preferences, etc.] ";
        return (previousActivity != null && previousActivity.equals("my.package.ActivityC"));
    }

    private void startActivity(Activity caller, Class newActivityClass)
    {
        Intent intent = new Intent(caller, newActivityClass);
        intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
        caller.startActivity( intent );
    }
}

// This is the default activity in the AndroidManifest.xml
// This prevents ActivityA from starting right away if the UIController
// wants to rebuild the stack.
public class LauncherActivity() {
    protected void onCreate(Bundle data) {
        super.onCreate(data);
        UIController.getInstance().onActivityCreated(this);
        finish();
    }
}

public class ActivityA() {
    protected void onCreate(Bundle data) {
        super.onCreate(data);
        UIController.getInstance().onActivityCreated(this);
    }
    protected void onResume() {
        super.onResume();
        UIController.getInstance().onActivityResumed(this);
    }
}

public class ActivityB() {
    // onCreate() & onResume(), same as ActivityA
}

public class ActivityC() {
    // onCreate() & onResume(), same as ActivityA
}

public class LauncherActivity() {
    protected void onCreate(Bundle data) {
        super.onCreate(data);
        UIController.getInstance().onActivityCreated(this);
        finish();
    }
}

public class ActivityA() {
    protected void onCreate(Bundle data) {
        super.onCreate(data);
        UIController.getInstance().onActivityCreated(this);
    }
    protected void onResume() {
        super.onResume();
        UIController.getInstance().onActivityResumed(this);
    }
}

public class ActivityB() {
    // same as ActivityA
}

public class ActivityC() {
    // same as ActivityA
}

Если у кого-то есть лучшее решение, не стесняйтесь публиковать его.


person David    schedule 26.07.2011    source источник
comment
stackoverflow.com/ вопросы/151777/   -  person ethrbunny    schedule 27.07.2011
comment
@David: Что было бы хорошим подходом к этой ситуации - хороший подход к этой ситуации - предложить навигацию через панель действий или меню параметров и позволить кнопке «Назад» вести себя естественно. Хотя вы хотите, чтобы кнопка «Назад» работала именно так, я сомневаюсь, что у вас есть доказательства того, что пользователь этого хочет. Из того, что я видел, пользователи хотят, чтобы НАЗАД означало НАЗАД (т. е. возврат к экрану, на котором они только что находились), и они жалуются, когда приложения не соблюдают эту базовую концепцию (например, отключают кнопку НАЗАД, захватывают кнопку НАЗАД, чтобы заниматься другими делами).   -  person CommonsWare    schedule 27.07.2011
comment
@ethrbunny: я мог бы использовать это, чтобы вручную отслеживать открытые действия, но мне интересно, существует ли более автоматическое решение.   -  person David    schedule 27.07.2011
comment
@CommonsWare: ну, в моем примере пользователь никогда не выходил из приложения. Поэтому я думаю, что для пользователя естественно работать с приложением, как если бы его сеанс никогда не заканчивался.   -  person David    schedule 27.07.2011
comment
Единственный случай, когда вы можете захватить кнопку «Назад», — это когда вы даете пользователю возможность заставить ее вести себя таким образом. Например: в приложении «Сообщения» в Cyanogen есть опция (по умолчанию отключена), которая заставляет кнопку «Назад» переходить к списку сообщений в цепочке сообщений. Это приемлемо, поскольку по умолчанию отключено.   -  person Michael Allen    schedule 27.07.2011
comment
@David: в Android пользователи не могут контролировать, работает приложение или нет. Они открывают приложение, а затем покидают его, и ОС сама выбирает, когда закрыть приложение. Поскольку это так, вы должны рассматривать каждый раз, когда пользователь переключается на ваше приложение, как новый сеанс, а не пытаться манипулировать обратным стеком.   -  person Michael Allen    schedule 27.07.2011
comment
@Майкл Аллен: мне не обязательно нажимать кнопку «Назад» .. Я только для восстановления стека открытых действий.   -  person David    schedule 27.07.2011
comment
@Майкл Аллен: как узнать, когда будет новая сессия?   -  person David    schedule 27.07.2011
comment
В Android, если приложение было закрыто ОС, то при следующем его открытии будет новый сеанс, поскольку приложение не было запущено заранее. Если он не был закрыт ОС, ваш код продолжит работу с того места, где он был остановлен в последний раз. Несмотря на это, кнопку «Назад» следует оставить по умолчанию.   -  person Michael Allen    schedule 27.07.2011
comment
@Micael Allen: что не так с восстановлением обратного стека, если ОС закрыла приложение?   -  person David    schedule 27.07.2011
comment
То, что вы описываете как желаемое поведение, является поведением Android по умолчанию. Если это не так, ваш код делает что-то, чтобы предотвратить это.   -  person cyngus    schedule 04.08.2011


Ответы (1)


Похоже, вы должны установить для этого параметра значение true и позволить Android управлять стеком действий.

android:alwaysRetainTaskState

Если для этого атрибута установлено значение «true» в корневом действии задачи, только что описанное поведение по умолчанию не происходит. Задача сохраняет все действия в своем стеке даже после длительного периода времени.

person CrackerJack9    schedule 04.08.2011
comment
отлично! Судя по описанию, это то, что я ищу. Я попробую! - Спасибо - person David; 05.08.2011
comment
CrackerJack9: меня подтолкнули к более срочным проектам, и у меня никогда не было времени поэкспериментировать с ними. Однако я принимаю ваш ответ, поскольку он звучит так, как будто он должен решить эту проблему. - person David; 15.09.2011
comment
Это не устраняет проблему, описанную в вопросе. - person bond; 10.12.2013
comment
@bond Вы задали отдельный вопрос (с полезной информацией, некоторые из которых могут отличаться от ОП)? - person CrackerJack9; 15.01.2016
comment
@ CrackerJack9 Понятия не имею. Прошло 2 года. - person bond; 15.01.2016