11 апреля 2014 г.

Задачи и обратный стек (Tasks and back stack). Часть 4

И так возвращаемся к той точке нашей темы, на которой мы чуть отклонились от нее, рассматривая тему сохранения состояния Активности.

Еще раз напомню полезные ссылки по теме:

http://startandroid.ru/ru/uroki/vse-uroki-spiskom/190-urok-116-povedenie-activity-v-task-intent-flagi-launchmode-affinity

http://habrahabr.ru/post/186434/

http://startandroid.ru/ru/uroki/vse-uroki-spiskom/62-urok-25-task-chto-eto-takoe-i-kak-formiruetsja.html

http://startandroid.ru/ru/uroki/vse-uroki-spiskom/190-urok-116-povedenie-activity-v-task-intent-flagi-launchmode-affinity

Частично, на случай если вышеперечисленные статьи попецаюца, я приведу текст из них, но в основном будет мой вольный перевод этой темы из альмаматер.

Управление задачами (ManagingTasks)


Метод, который использует Андроид, по умолчанию, для управления задачами и обратным стеком, помещая все активности в той последовательности как они были вызваны в стек типа “последний вошел, первый вышел”, работает замечательно для большинства приложений и вы можете не беспокоится о том как Активности связаны с задачами и каким образом они существуют в обратном стеке. Однако вы можете изменить это поведение по умолчанию. Возможно вы захотите чтобы стартуя, Активность в вашем приложении, запускала новую задачу, вместо того чтобы быть помещенной в текущую задачу. Или же, стартуя Активность, вы хотите перевести на передний план существующий экземпляр Активности вместо создания нового экземпляра на вершине стека. Или же вы хотите, чтобы ваш обратный стек был очищен от всех Активностей исключая корневую Активность, когда пользователь покидает задачу.

Все эти и другие варианты вы можете реализовать двумя способами: используя атрибуты Активности (элемент <activity>) в AndroidManifest.xml или же используя флаги в интенте (Intent) который вы передаете методу startActivity().

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

Атрибуты свойства <activity> в AndroidManifest.xml которые вы можете использовать:


Флаги интентов, которые вы можете использовать:


Далее мы все это рассмотрим более подробно.

Предупреждение: Для большинства приложений не требуется вмешательства в поведение по умолчанию Активностей и задач. Если вы все же решили, что такая необходимость для вашей Активности существует, то тщательно протестируйте ее поведение и удобство использования во время ее запуска, возврата к ней из других активностей и задач используя кнопку ОБРАТНО. Тестируя ее поведение убедитесь, что навигация работает так, как может ожидать пользователь.

Определение способов запуска


Методы запуска позволяют вам определить как новый экземпляр Активности будет связан с текущей задачей. Как уже говорилось вы можете это определить двумя способами.

1) Используя файл манифеста

Когда вы описываете Активность в файле манифеста, вы можете указать как она будет связана с задачами, когда она будет запущена.

2) Используя флаги

Когда вы вызываете метод startActivity(), вы можете установить флаги в интенте (intent), которые указывают как новая Активность будет связана с текущей задачей.

Еще раз напомним что, если Активность А, запускает Активность В, у Активности В может быть определено в файле манифеста как она будет связана с текущей задачей, в тоже время Активность А, тоже может определить как Активность В должна быть связана с текущей задачей. Если обе Активности имеют определение как Активность В должна быть связана с задачей, тогда, в этом случае приоритет определения будет иметь Активность А (поскольку это было определено в интенте), несмотря на то, что у Актвиности В, в манифесте может быть прописано другое поведение.

На заметку: Некоторые методы, доступные в файле манифеста, могут быть не доступны как флаги для интента, аналогично этому, некоторые флаги для интента, не могут быть определены в файле манифеста.

Использование файла манифеста


Когда вы описываете активность в вашем файле манифеста, вы можете определить как активность будет связана с задачей используя атрибут launchMode.

Атрибут launchMode определяет как активность будет связана с задачей при запуске. Существует четыре метода которые вы можете назначить атрибуту launchMode.

  • “standard” – (по умолчанию). Система создает новый экземпляр активности в задаче из которой она была запущена и направляет интент на нее. Может быть много экземпляров Активности. Каждый экземпляр может принадлежать различным задачам. И любая задача может иметь множество экземпляров этой Активности.
  • “singleTop” – если экземпляр активности уже существует на вершине текущей задачи, то система направляет интент на этот экземпляр хотя, и, хотя и вызывает метод onNewIntent(), новый экземпляр Активности создаваться не будет. Новый экземпляр Активности создается в стеке только в том случае, если экземпляра данной Активности нет на вершине стека. Может быть много экземпляров Активности. Каждый экземпляр может принадлежать различным задачам. И любая задача может иметь множество экземпляров этой Активности.

Например, предположим что в задаче есть обратный стек, состоящий из корневой Активности А, а так же с Активностями B,C и D на вершине стека (стек A-B-C-D; D – на вершине). Интент вызывает Активность D. Если Активность D имеет метод запуска по умолчанию (“standard”), то будет создан новый экземпляр Активности D и стек будет иметь вид A-B-C-D-D. Однако, если в Активности D определен метод запуска “singleTop”, то существующий экземпляр Активности D получит фокус и останется на вершине стека и стек будет продолжать иметь вид A-B-C-D. Однако, если какой-либо интент вызовет Активность B, то новый экземпляр Активности B будет помещен на вершину стека и стек будет иметь вид A-B-C-D-B, даже не смотря на то что, запуск Активности D определен как “singleTop”.

На заметку: Когда создается новый экземпляр Активности, пользователь может, нажав кнопку ОБРАТНО, вернуться к предыдущей Активности. Но когда существующий экземпляр Активности получает фокус через onNewIntent(), то пользователь не может нажав кнопку ОБРАТНО, вернуться к тому же состоянию Активности, в которой он пребывал до вызова onNewIntent().

  • “singleTask” – если Активность была запущенна из другого приложения, система создает новую задачу и устанавливает эту Активность корневой для нее. Однако, если экземпляр Активности уже существует в какой либо другой задаче, то система направляет интент на существующий экземпляр Активности через вызов метода onNewIntent(), вместо того чтобы создавать новый экземпляр Активности. Все Активности, находящиеся в стеке над ней уничтожаются. Только один экземпляр такой Активности может существовать.

На заметку: Если Активность singleTask запускается из своего же приложения, то новая задача не создается.

  • “singleInstance” – это тоже самое что и “singleTask”, но система уже не может создавать другие Активности в задаче где существует экземпляр такой Активности. Такая Активность всегда является одним и единственным членом в своей задаче. Любые Активности, запускаемы из такой Активности, будут запускаться в отдельных задачах.

Еще один пример, приложение родного браузера в Андроид объявляет, через параметр “singleTask”,  что Активности браузера должны всегда открываться в его собственной задаче. Это означает что если ваше приложение передает интент для открытия браузера Андроид, то Активность браузера не помещается в задачу вашего приложения. Вместо этого запускается новая задача браузера или же если задача браузера уже существует в фоне, то она получает фокус и переходит на передний план благодаря вызову метода onNewIntent().

Независимо от того, в какой задаче открыта новая Активность. При нажатии кнопки ОБРАТНО мы все равно вернемся на предыдущую задачу и предыдущую Активность. Единственный момент, который нужно учитывать — это параметр singleTask. Если при открытии такой Активности мы достанем ее из другого задачи пребывающей в фоновом режиме, то мы полностью переключаемся стек данной задачи. На картинке ниже это продемонстрировано.

diagram_backstack_singletask_multiactivity

На заметку: Поведение которое вы определяете для своей Активности через атрибут launchMode, может быть переопределено через флаги при вызове в интенте запускающем вашу Активность.

Использование Intent флагов


Мы можем устанавливать специальный флаги для Intent, который запускает новую Activity. Флаги более приоритетны, чем launchMode. Существует несколько флагов:
  • FLAG_ACTIVITY_NEW_TASK – запускает Активность в новой задаче. Если же уже существует задача с экземпляром Активности которую вы хотите запустить, то она получает фокус через метод onNewIntent(), выходит на передний план и восстанавливает состояние Активности. Этот флаг полностью аналогичен параметру launchMode “singleTask”.

    Это то как написано в альма-матер, но на самом деле все обстоит несколько иначе. Даже совсем иначе.

    Данный флаг действительно всегда запускает Активность в новой задаче и происходит переключение на стек этой задачи. На этом сходство с launchMode “singleTask” и заканчивается!

    Активность с данным флагом запускается в новой задаче, только в том случае если запускаемая Активность принадлежит другому приложению. Если же запускающая и запускаемая с этим флагом Активность принадлежат одному приложению, то Активность не запускается в новой задаче, а запускается в задаче своего приложения.

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

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

    А если же запускаемая Активность уже существует в задаче своего приложения, то при ее вызове из другого приложения будет создан новый экземпляр данной Активности в задаче своего приложения. То есть опять же, переключения на вызываемую Активность не произойдет.

    Более подробно можно почитать  тут и тут.
  • FLAG_ACTIVITY_SINGLE_TOP – если Activity запускает сама себя, т.е. она находится в вершине стека, то вместо создания нового экземпляра в стеке, текущая Активность получает фокус через вызов метода onNewIntent(). Флаг аналогичен параметру launcMode “singleTop”.
  • FLAG_ACTIVITY_CLEAR_TOP – если экземпляр данной Activity уже существует в стеке данного таска, то все Activity, находящиеся поверх нее разрушаются и этот экземпляр становится вершиной стека. Также вызовется через onNewIntent(). В launcMode не существует аналога данному флагу.

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

Примечание: Если launchMode запускаемой Активности является “standard”, то экземпляр этой Активности будет удален из стека и будет создан новый экземпляр данной Активности. Это происходит потому что для Активности с launchMode=“standard” всегда создается новый экземпляр при вызове new Intent.

Пока не этом теорию закончим и перейдем к практике. Затем продолжим рассматривать данную тему с пункта Handling affinities.

Комментариев нет:

Отправить комментарий