В чем разница между 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, то это намерение будет доставлено в onNewIntent() текущего экземпляра.

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

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


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

Если он установлен в намерении, переданном в 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,

3 ответа

Решение

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

Вот (хотя и неубедительный) пример:

В приложении А у нас есть корневой Активность RootA и у нас есть еще одна активность 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 сейчас запускает активность ReparentableA стандартным способом, без каких-либо специальных флагов. Экземпляр ReparentableA создан и помещен поверх RootB в текущей задаче.

Нажмите ДОМОЙ.

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

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


Примечания по использованию Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP

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


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

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


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

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

  • Создание новой задачи (в этом случае функциональность "сброса" включает в себя переопределение задачи, описанное выше), или
  • Если 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 выясняет, что делать.

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


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

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

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

1) FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

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

2) FLAG_ACTIVITY_CLEAR_TOP

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

3) FLAG_ACTIVITY_SINGLE_TOP

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

Другие вопросы по тегам