Backstack android как посмотреть

Tasks и Back Stack

Task — это набор активити, с которыми пользователь взаимодействует при использовании какого-либо приложения. У каждого task’а есть свой back stack — это что-то вроде способа организации открытых пользователем активити, который устроен по принципу LIFO — “последним вошел — первым вышел”. То есть при открытии новой активити, она становится вершиной стека, а предыдущая уходит в состояние “остановлена”. При нажатии пользователем на кнопку Back, новая активити уничтожается и удаляется из стека, а предыдущая восстанавливается (возвращается в состояние “возобновлена”). Если продолжать нажимать на кнопку Back, то в итоге пользователь вернётся на главный экран устройства, а task перестанет существовать. Если же пользователь свернёт приложение, то task продолжит существовать в фоне и хранить весь свой стек, при этом все активити перейдут в состояние “остановлена”. Поэтому пользователь в любой момент сможет вновь открыть приложение и продолжить работу с того, на чём остановился. Однако такой task может быть удален системой при нехватке ресурсов.

Может возникнуть вопрос: а как же фрагменты? Как они сохраняются в стеке? У них всё устроено несколько иначе, чем у активити: фрагмент помещается в back stack, управляемый активити и то, только если был вызван соответствующий метод ( addToBackStack() ) во время транзакции.

В версии Android 7.0 была добавлена поддержка многооконного режима: пользователь может разделить экран и таким образом работать с несколькими приложениями. В таком режиме система управляет task’ами отдельно для каждого окна, т.е. у каждого окна может быть несколько task’ов.

Визуально task’и можно увидеть на экране последних запущенных задач:

Управление task’ами

Некоторые приложения спроектированы таким образом, что есть несколько точек перемещения к одной и той же активити. Несмотря на то, что такая активити уже может находится в стеке, каждый раз будет создаваться её новый экземпляр и также сохраняться в стек. Таким образом, когда пользователь решит переместиться к самой первой активити, он увидит все открытые им, казалось бы одинаковые активити, но в разном состоянии. Подобного эффекта можно избежать при помощи специальных атрибутов манифеста и флагов для Intent.

Обратите внимание, что иногда атрибуты в манифесте и флаги в Intent могут противоречить друг другу. В этом случаи флаги Intent будут более приоритетны.

Атрибуты

launchMode
Данный атрибут можно указать для каждой активити в манифесте. Имеет несколько значений:

  • standard — режим по умолчанию. Активити может быть создана несколько раз, при этом каждый экземпляр может находится в разных task’ах, а каждый task содержать несколько её экземпляров.
  • singleTop — если активити на данный момент является вершиной стека, то вместо создания нового экземпляра у нее сработает метод onNewIntent() . Если активити не является вершиной стека, то будет создан и помещён в стек её новый экземпляр. Активити может быть создана несколько раз, при этом каждый экземпляр может находится в разных task’ах, а каждый task содержать несколько её экземпляров.
  • singleTask — создает новый task и устанавливает активити корневой для него, но только в случае, если экземпляра данной активити нет ни в одном другом task’е. Если активити уже расположена в каком либо task’е, то откроется именно тот экземпляр активити и для неё будет вызван метод onNewIntent() . Она в свою очередь становится главной, а все верхние экземпляры удаляются (если они есть). При этом, если активити была вытащена из фонового task’а, то мы переключимся на этот task и его стек. Только один экземпляр такой активити может существовать
  • singleInstance — тоже что и singleTask , но для данной активити всегда будет создаваться отдельный task и она будет в ней корневой. Данное значение указывает, что активити будет одним и единственным членом своего task’а. Все активити, запускаемые посредством такой активити будут открываться в отдельном task’е.

taskAfinity
Позволяет изменять поведение активити, например, чтобы в одном приложении активити работали в разных task’ах или активити разных приложений работали в одном. Для этого в манифесте для каждой активити указывается название task’а с помощью атрибута taskAfinity . Имя task’а должно быть отличным от имени пакета, объявленного в манифесте. Данный параметр будет работать при следующих обстоятельствах:

  • Intent, который запускает активити, содержит флаг FLAG_ACTIVITY_NEW_TASK . По умолчанию новая активити запускается в том же task’е, что и активити, из которой она была запущена. А данный флаг заставляет систему искать другой task для её размещения (по имени, указанному в атрибуте taskAfinity ). Если нужный task уже существует, то активити будет помещена в него. Если нет, то запуститься новый task.
  • У запускаемой активити установлен атрибут allowTaskReparenting = true . Этот атрибут означает, что активити может перемещаться между task’ом, который ее вызвал, и task’ом, который указан в taskAfinity — в зависимости от того, какой task сейчас активен.
Читайте также:  Linux and android apps

Флаги

Флаги устанавливаются для Intent, который открывает новую активити при помощи метода startActivity() . Флаги приоритетнее атрибутов в манифесте.

Виды флагов:

  • FLAG_ACTIVITY_NEW_TASK — запускает активити в новом task’е. Если уже существует task с экземпляром данной активити, то этот task выводится на передний план, активити восстанавливает своё последнее состояние и для неё срабатывает метод onNewIntent() . Данный флаг аналогичен значению singleTask атрибута launchMode .
  • FLAG_ACTIVITY_SINGLE_TOP — если активити запускает сама себя, то есть она находится в вершине стека, то вместо создания нового экземпляра в стеке вызывается метод onNewIntent() . Данный флаг аналогичен значению singleTop атрибута launchMode .

FLAG_ACTIVITY_CLEAR_TOP — если экземпляр данной активити уже существует в стеке данного task’а, то все активити, находящиеся поверх нее разрушаются и этот экземпляр становится вершиной стека. Также вызовется метод onNewIntent() .

FLAG_ACTIVITY_CLEAR_TOP чаще всего используется вместе с FLAG_ACTIVITY_NEW_TASK . При совместном использовании эти флаги позволяют найти существующую активити в другом task’е и поставить её в такое положение, в котором она сможет реагировать на полученный Intent.

Очистка стека

Если task долгое время находится в фоне, то система сама чистит его стек, оставляя только корневую активити. Подобное поведение объясняется тем, что по прошествии длительного времени пользователь, вероятно, забыл, что он делал в приложении и открыл его повторно уже с иной целью.

У активити существует три атрибута для изменения такого поведения:

  • alwaysRetainTaskState — если значение этого атрибута для корневой активити true , то стек не будет чиститься и полностью восстановится даже после длительного времени.
  • clearTaskOnLaunch — если значение этого атрибута для корневой активити true , то стек будет чиститься моментально, как только пользователь покинет task. Полная противоположность alwaysRetainTaskState . Пользователь всегда будет возвращаться к task’у в его начальном состоянии, даже если покинет task всего на мгновение.
  • finishOnTaskLaunch — атрибут похож на clearTaskOnLaunch , но он работает с одной активити, а не со всем task’ом. Если значение этого атрибута true , то активити будет частью task’а только в рамках текущего сеанса. Если пользователь покинет task, а затем вернётся — активити уже в нём не будет.

Источник

История в каждой вкладке или multiple backstack

Проблема

С выходом android 3.0 (Api Level 11) в android появились фрагменты, и так уж у разработчиков google получилось, что для них поддерживается только один backstack. Не всегда дизайнеры и заказчики хотят учитывать эту особенность. А иногда просто хотят полную копию уже существующего ios приложения.

Допустим нам нужно сделать подобие Tab Bar на android, в том числе с сохранением истории в каждой вкладке. Но у нас один backstack, и что же нам делать? Задача кажется невозможной.

Исследование

«Если не спросить, никогда не узнаешь. Если знаешь, нужно лишь спросить.»

С одной стороны это противоречит официальному guide (смотри Behavior), в котором однозначно написано, что навигация через низ должна сбрасывать состояние.
Но кого это волнует, когда речь идёт об удобстве пользователя? Положа руку на сердце вы признаете, что так, как рекомендуется — удобнее?

И как же всё-таки сделать, что казалось бы невозможно из-за ограничения платформы? На ум приходят несколько вариантов один сложнее другого, а поиск в google выдаёт и того больше ужасных костыльных решений.

Но зачем гадать, если можно подсмотреть как сделано у Instagram? К слову сказать, Instagram так работал не всегда, были времена, когда дизайн Instagram был с вкладками, а история при переключении сбрасывалась.

Декомпилируем apk Instagram при помощи apktool и смотрим что там. Главное activity приложения — com.instagram.android.activity.MainTabActivity, смотрим от чего она унаследована, — от класса com/instagram/base/activity/tabactivity/a, который в свою очередь унаследован от android/app/ActivityGroup. Дальше можно не копать.

Такие классы, как ActivityGroup, TabActivity, LocalActivityManager — deprecated с 13 Api Level, тоесть почти сразу, как появились фрагменты. На developer.android.com для этих классов написано следующее:

This class was deprecated in API level 13.
Use the new Fragment and FragmentManager APIs instead; these are also available on older platforms through the Android compatibility package.

Все знают, что использовать deprecated в новой разработке нехорошо. Все бросились писать на фрагментах и классы были преданы забвению.

Решение?

Пожалуй, это единственное рабочее решение. Оно работает «из коробки», никаких костылей (deprecated ведь не считается). Лично я просто забыл про LocalActivityManager, хотя начал разрабатывать под android ещё в те времена, когда телефонов с 8 Api Level было больше, чем остальных, но они активно вытеснялись.

Везде настолько упорно утверждают, что фрагменты наше всё, а тенденция разработки single activity application столь непреложна, что те, кто присоединился к разработке на android после 2011, скорее всего просто ничего и не слышали о LocalActivityManager.

Это простое решение, глупо им не воспользоваться. В каждой вкладке у нас будет своя activity со своим жизненным циклом, а главное своим backstack’ом!

Немного кода

Использовать TabHost просто. Если знать, что искать, можно найти много древних туториалов, как им пользоваться. Интернет помнит.

Layout для нашего главного activity:

Очень жаль, что приходится использовать deprecated классы, но пока google не сделает другого решения — это единственный адекватный вариант. Можно построить удобную навигацию с одним backstack’ом, есть другие разумные ограничения платформы, которые необходимо учитывать и которые обоснованны, но в данном случае кажется, что google попросту упустил такую возможность при проектировании Fragment API.

Так уж получается, что в этом моменте android оказался уж точно не круче iphone…

Источник

Tasks и Back Stack в Android

Итак. Каждое Android приложение, как минимум, состоит из фундаментальных объектов системы — Activity. Activity — это отдельный экран который имеет свою отдельную логику и UI. Количество Activity в приложении бывает разное, от одного до много. При переходах между различными Activity пользователь всегда может вернуться на предыдущую, закрытую Activity при нажатии кнопки back на устройстве. Подобная логика реализована с помощью стека (Activity Stack). Его организация «last in, first out» — т.е. последний вошел, первый вышел. При открытии новой Activity она становится вершиной, а предыдущая уходит в режим stop. Стек не может перемешиваться, он имеет возможность добавления на вершину новой Activity и удаление верхней текущей. Одна и та же Activity может находиться в стеке, сколько угодно раз.
Task — это набор Activity. Каждый таск содержит свой стек. В стандартной ситуации, каждое приложение имеет свой таск и свой стек. При сворачивании приложения, таск уходит в background, но не умирает. Он хранит весь свой стек и при очередном открытии приложения через менеджер или через launcher, существующий таск восстановится и продолжит свою работу.

Ниже покажу картинку, как работает стек.

Если продолжать нажимать кнопку back, то стек будет удалять Activity до того, пока не останется главная корневая. Если же на ней пользователь нажмет back, приложение закроется и таск умрет. Кстати, я говорил о том, что когда мы сворачиваем наше приложение и запускам например новое, то наш таск просто уходит в background и будет ждать момента, пока мы его вызовем. На самом деле есть одно «но». Если мы будем иметь много задач в background или же просто сильно нагружать свое устройство, не мала вероятность того, что таск умрет из за нехватки системных ресурсов. Это не война конечно, но то что мы потеряем все наши текущие данные и наш стек очистится — это точно. Кстати для избежания потери данных в таком случаи, вам стоит почитать про SavingActivityState.

Маленький итог

Управление тасками

Существует два пути для изменения стандартной организации тасков. Мы можем устанавливать специальные атрибуты в манифесте для каждой Activity. Также мы можем устанавливать специальные флаги для Intent, который запускает новую Activity с помощью startActivity(). Заметьте, что иногда атрибуты в манифесте и флаги в Intent могут противоречить друг другу. В этом случаи флаги Intent будут более приоритетны.

Атрибут launchMode

Для каждой Activity в манифесте можно указать атрибут launchMode. Он имеет несколько значений:

  • standard — (по умолчанию) при запуске Activity создается новый экземпляр в стеке. Activity может размещаться в стеке несколько раз
  • singleTop — Activity может распологаться в стеке несколько раз. Новая запись в стеке создается только в том случаи, если данная Activity не расположена в вершине стека. Если она на данный момент является вершиной, то у нее сработает onNewIntent() метод, но она не будет пересоздана
  • singleTask — создает новый таск и устанавливает Activity корнeвой для него, но только в случаи, если экземпляра данной Activity нет ни в одном другом таске. Если Activity уже расположена в каком либо таске, то откроется именно тот экземпляр и вызовется метод onNewIntent(). Она в свое время становится главной, и все верхние экземпляры удаляются, если они есть. Только один экземпляр такой Activity может существовать
  • singleInstance — тоже что и singleTask, но для данной Activity всегда будет создаваться отдельный таск и она будет в ней корневой. Данный флаг указывает, что Activity будет одним и единственным членом своего таска

На самом деле не важно, в каком таске открыта новая Activity. При нажатии кнопки back мы все равно вернемся на предыдущий таск и предыдущие Activity. Единственный момент, который нужно учитывать — это параметр singleTask. Если при открытии такой Activity мы достанем ее из другого background таска, то мы полностью переключаемся на него и на его стек. на картинке ниже это продемонстрировано.

Флаги

Как и говорил, мы можем устанавливать специальный флаги для Intent, который запускает новую Activity. Флаги более приоритетны, чем launchMode. Существует несколько флагов:

  • FLAG_ACTIVITY_NEW_TASK — запускает Activity в новом таске. Если уже существует таск с экземпляром данной Activity, то этот таск становится активным, и срабатываем метод onNewIntent().
    Флаг аналогичен параметру singleTop описанному выше
  • FLAG_ACTIVITY_SINGLE_TOP — если Activity запускает сама себя, т.е. она находится в вершине стека, то вместо создания нового экземпляра в стеке вызывается метод onNewIntent().
    Флаг аналогичен параметру singleTop описанному выше
  • FLAG_ACTIVITY_CLEAR_TOP — если экземпляр данной Activity уже существует в стеке данного таска, то все Activity, находящиеся поверх нее разрушаются и этот экземпляр становится вершиной стека. Также вызовется onNewIntent()

Affinity

Стандартно все Activity нашего приложения работают в одном таске. По желанию мы можем изменять такое поведение и указывать, чтобы в одном приложении Activity работали в разных тасках, или Activity разных приложений работали в одном. Для этого мы можем в манифесте для каждой Activity указывать название таска параметром taskAffinity. Это строковое значение, которое не должно совпадать с названием package, т.к. стандартный таск приложения называется именно как наш пакет. В общем случаи данный параметр указывает, что Activity будет гарантированно открываться в своём отдельном таске. Данный параметр актуален, если мы указываем флаг FLAG_ACTIVITY_NEW_TASK или устанавливаем для Activity атрибут allowTaskReparenting=«true». Этот атрибут указывает, что Activity может перемещаться между тасками, который её запустил и таском, который указан в taskAffinity, если один из них становится активным.

Чистка стека

Если таск долгое время находится в background, то система сама чистит его стек, оставляя только корневую Activity. Данное поведение обусловлено тем, что пользователь может забыть, что он делал в приложении до этого и скорее всего зашел в него снова уже с другой целью. Данная логика также может быть изменена с помощью нескольких атрибутов в манифесте.

  • alwaysRetainTaskState — если флаг установлен в true для корневой Activity, то стек не будет чиститься и полностью восстановится даже после длительного времени
  • clearTaskOnLaunch — если установить флаг в true для корневой Activity, то стек будет чиститься моментально, как только пользователь покинет таск. Полная противоположность alwaysRetainTaskState
  • finishOnTaskLaunch — работает аналогично clearTaskOnLaunch, но может устанавливаться на любую Activity и удалять из стека именно её

Это всё для данного топика. Статья не импровизированная, а по сути является вольным переводом официальной документации. Рекомендую собрать легкий пример и поэксперементировать с флагами и атрибутами. Некоторые моменты, лично для меня были, неожиданно интересными. любые ошибки и недоработки учту в лс. Спасибо.

Источник

Читайте также:  Android как проверить трафик
Оцените статью