Каковы различия между FLAG_ACTIVITY_RESET_TASK_IF_NEEDED и FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP?

Я нахожусь в процессе (наконец) написания главы о задачах для моей книги, и я сталкиваюсь с несколькими затяжными головоломками.

Вещи, которые служат в качестве стартовых экранных пусковых установок, похоже, используют комбинацию FLAG_ACTIVITY_NEW_TASK и FLAG_ACTIVITY_RESET_TASK_IF_NEEDED, когда они запускают запрошенную активность запуска:

Intent i=new Intent(Intent.ACTION_MAIN);

i.addCategory(Intent.CATEGORY_LAUNCHER);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
i.setComponent(name);

startActivity(i);  

Документация для FLAG_ACTIVITY_RESET_TASK_IF_NEEDED имеет:

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

Это не особенно понятно.

В частности, казалось бы, те же эффекты будут видны с использованием комбинации FLAG_ACTIVITY_CLEAR_TOP и FLAG_ACTIVITY_SINGLE_TOP. Цитирование документов для FLAG_ACTIVITY_CLEAR_TOP:

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

Текущий исполняемый экземпляр [желаемого действия] получит либо новое намерение, которое вы начинаете здесь, в методе onNewIntent(), либо сам закончите и перезапустите с новым намерением. Если он объявил свой режим запуска "множественным" (по умолчанию), и вы не установили FLAG_ACTIVITY_SINGLE_TOP в одном и том же намерении, он будет завершен и заново создан; для всех других режимов запуска или если установлен FLAG_ACTIVITY_SINGLE_TOP, этот Intent будет доставлен в текущий экземпляр onNewIntent().

Документация FLAG_ACTIVITY_CLEAR_TOP имеет смысл, по крайней мере для меня.

Итак, что делает FLAG_ACTIVITY_RESET_TASK_IF_NEEDED, что отличается от комбинации FLAG_ACTIVITY_CLEAR_TOP и FLAG_ACTIVITY_SINGLE_TOP?


Бонусные баллы, если вы можете объяснить, что FLAG_ACTIVITY_CLEAR_TASK отличается от любого из двух других параметров, описанных выше.

Если задано в Intent, переданном Context.startActivity(), этот флаг приведет к тому, что любая существующая задача будет связана с активностью, подлежащей очистке, до начала действия. То есть, деятельность становится новым корнем пустой задачи, и все старые действия завершены. Это можно использовать только совместно с FLAG_ACTIVITY_NEW_TASK.

Одно очевидное различие между этим и FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP заключается в том, что FLAG_ACTIVITY_CLEAR_TASK требуется FLAG_ACTIVITY_NEW_TASK. Но, кроме этого, казалось бы, что сетевые эффекты одинаковы, а также соответствуют FLAG_ACTIVITY_RESET_TASK_IF_NEEDED.

Ответ 1

Я посмотрел исходный код для ActivityManager. Флаг Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED действительно выполняет некоторую магию, которая не выполняет Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP: она запускает задачу reparenting.

Здесь пример (хотя и хромой):

В приложении А мы имеем корневую активность RootA, и у нас есть другая Activity ReparentableA:

<application
        android:label="@string/app_name">
    <activity android:name=".RootA">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>
    <activity android:name=".ReparentableA"
            android:allowTaskReparenting="true"/>
</application>

Приложение A имеет имя пакета "com.app.a", поэтому по умолчанию taskAffinity его компонентов "com.app.a".

В приложении B мы имеем корневую активность RootB:

<application
        android:label="@string/app_name">
    <activity android:name="RootB">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>
</application>

Приложение B имеет имя пакета "com.app.b", поэтому по умолчанию taskAffinity его компонентов "com.app.b".

Теперь мы запускаем приложение B с экрана HOME. Это запустит новую задачу и создаст новый экземпляр Activity RootB в качестве основной активности в этой задаче. Активность RootB теперь запускает Activity ReparentableA стандартным образом без каких-либо специальных флагов. Создается экземпляр ReparentableA и помещается поверх RootB в текущую задачу.

Нажмите HOME.

Теперь мы запускаем приложение А с экрана HOME. Это запустит новую задачу и создаст новый экземпляр Activity RootA в качестве корневой активности в этой задаче. ПРИМЕЧАНИЕ. Когда Android запускает "Launcher" Intent, он автоматически устанавливает флаги Intent.FLAG_ACTIVITY_NEW_TASK и Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED. Из-за этого запуск RootA теперь вызывает повторное рассмотрение задачи. Android ищет, есть ли какие-либо действия в каких-либо других задачах, которые имеют сродство к этой новой задаче (и являются reparentable). Он находит ReparentableA (с той же близостью задачи, что и RootA) в задаче приложения B, и переводит его в новую задачу App A. При запуске приложения А мы не видим RootA, на самом деле видим ReparentableA, поскольку он перемещается в начало новой задачи.

Если мы вернемся в приложение B, мы увидим, что ReparentableA ушел из стека задач, и эта задача теперь состоит только из одного действия: RootB.


Заметки об использовании Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP

Важно помнить об использовании этих флагов для "reset задачи" в том, что работает только в том случае, если в корне задачи уже есть экземпляр целевого Activity. Если ваша корневая активность когда-либо заканчивается, вы не можете очистить свою задачу, запустив корневую активность с помощью Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP. Android просто создаст новый экземпляр целевого (корневого) Activity и поместит его поверх существующих действий в задачу, что, вероятно, совсем не так, как вы хотите.


Разница между Intent.FLAG_ACTIVITY_CLEAR_TASK и Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP:

Как отмечено выше, использование CLEAR_TOP | SINGLE_TOP работает только в том случае, если в задаче уже есть экземпляр целевого Activity. CLEAR_TASK, однако, удаляет все действия из задачи, независимо от того, был ли экземпляр целевого Activity в задаче. Кроме того, использование CLEAR_TASK гарантирует, что целевая активность станет корневой операцией задачи, без необходимости знать, какая активность была корневой активностью перед очисткой задачи.


Разница между Intent.FLAG_ACTIVITY_CLEAR_TASK и Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED:

Как указано выше, использование CLEAR_TASK будет всегда удалять все действия из задачи и запускать новый экземпляр целевой активности. Напротив, RESET_TASK_IF_NEEDED выполняет только reset задачу в определенных ситуациях (часть "IF_NEEDED" ). Задача - "reset" , если Android:

  • Создание новой задачи (в этом случае функциональность "reset" включает объяснение задачи, описанную выше) или
  • Если Android выводит фоновую задачу на передний план (в этом случае задача удаляется только из всех действий, запущенных с помощью Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET и любых действий, которые находятся поверх этих действий). ПРИМЕЧАНИЕ. В этом случае корневая активность никогда не очищается.

ВАЖНОЕ ПРИМЕЧАНИЕ:. Когда вы тестируете, обратите внимание, что существует разница в том, как Android ведет себя при запуске приложений с экрана HOME (или из списка доступных приложений) и при выборе задач из недавнего списка задач.

В первом случае (запуск приложения, выбирая его из списка доступных приложений или из ярлыка на экране HOME) создается пусковая установка Intent с Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED. Это используется независимо от того, запущено ли приложение. Запускается Intent, а затем ActivityManager показывает, что делать.

Во втором случае (выбирая задачу из списка недавних задач), если задача все еще существует, она просто выводится на передний план. Задача "reset" НЕ выполняется, если задача просто перенесена на передний план, используя недавний список задач. Для меня не очевидно, как это управляется, и у меня не было возможности просмотреть исходный код, чтобы понять, почему это так.


Надеюсь, это ответит на ваши вопросы. Ждем ваших отзывов и результатов тестов.

Ответ 2

Возможно, я ошибаюсь, но в своем понимании FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED анализирует все задачи и гарантирует, что запущена только одна задача с активностью запуска.

FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP будет проверять только текущую задачу, поэтому вы можете запустить одновременно два экземпляра запуска (если первый был создан как отдельная задача).

Ответ 3

1) FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

Если какая-либо задача находится в ожидании, она уничтожит этот процесс и запустит запрошенную вами операцию.

2) FLAG_ACTIVITY_CLEAR_TOP

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

3) FLAG_ACTIVITY_SINGLE_TOP

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