Android: ошибка в launchMode="singleTask"? -> стек активности не сохранился
Моя основная деятельность A
имеет как установлено android:launchMode="singleTask"
в манифесте. Теперь, когда я начинаю другую деятельность оттуда, например B
и нажмите HOME BUTTON
на телефоне, чтобы вернуться на главный экран, а затем снова вернуться к моему приложению, либо нажав кнопку приложения, либо нажав HOME BUTTON
долго показывать мои последние приложения, это не сохраняет мой стек активности и возвращается прямо к A
вместо ожидаемой активности B
,
Здесь два поведения:
Expected: A > B > HOME > B
Actual: A > B > HOME > A (bad!)
Есть ли пропущенная настройка или это ошибка? Если последнее, есть ли обходной путь для этого, пока ошибка не будет исправлена?
К вашему сведению: этот вопрос уже обсуждался здесь. Тем не менее, похоже, что реального решения этой проблемы пока нет.
13 ответов
Это не ошибка. Когда существующий singleTask
активность запущена, все остальные действия над ней в стеке будут уничтожены.
Когда вы нажимаете HOME
и снова запустите действие, ActivityManger
вызывает намерение
{act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER]flag=FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_RESET_IF_NEEDED cmp=A}
В результате получается A > B > HOME > A.
Различно, когда A запускающим режимом является "Стандартный". Задача, содержащая А, выйдет на передний план и сохранит состояние в том же состоянии, что и раньше.
Вы можете создать "Стандартное" действие, например. C как средство запуска и startActivity(A) в методе onCreate C
ИЛИ ЖЕ
Просто удалите launchMode="singleTask"
и установить FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOP
флаг всякий раз, когда вызвать намерение A
Фром http://developer.android.com/guide/topics/manifest/activity-element.html на singleTask
Система создает действие в корне новой задачи и направляет намерение к ней. Однако, если экземпляр действия уже существует, система направляет намерение к существующему экземпляру посредством вызова его метода onNewIntent() вместо создания нового.
Это означает, что когда флаги action.MAIN и category.LAUNCHER нацелены на ваше приложение из Панели запуска, система скорее направит намерение к существующему ActivityA, чем к созданию новой задачи и установке нового ActivityA в качестве корневого. Скорее, он снесет все действия над существующей задачей, в которой находится ActivityA, и вызовет ее onNewIntent().
Если вы хотите зафиксировать как поведение singleTop, так и singleTask, создайте отдельное действие "делегировать" с именем SingleTaskActivity с помощью функции singleTask launchMode, которая просто вызывает действие singleTop в его onCreate(), а затем завершает себя. Действие singleTop по-прежнему будет содержать фильтры намерений MAIN/LAUNCHER, которые будут продолжать действовать как основное действие Launcher приложения, но когда другие действия желают вызвать это действие singleTop, оно должно вместо этого вызывать SingleTaskActivity, чтобы сохранить поведение singleTask. Намерение, передаваемое в действие SingleTop, также должно быть перенесено в действие SingleTop, поэтому что-то вроде следующего сработало для меня, так как я хотел иметь как режимы запуска SingleTask, так и SingleTop.
<activity android:name=".activities.SingleTaskActivity"
android:launchMode="singleTask">
public class SingleTaskActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
intent.setClass(this, SingleTop.class);
startActivity(intent);
}
}
И ваша однопользовательская активность продолжит работать в режиме однопользовательского запуска.
<activity
android:name=".activities.SingleTopActivity"
android:launchMode="singleTop"/>
Удачи.
Стефан, ты когда-нибудь нашел ответ на это? Я собрал тестовый пример для этого и вижу то же (недоумение) поведение... Я вставлю код ниже на случай, если кто-нибудь придет и увидит что-то очевидное:
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example" >
<uses-sdk android:minSdkVersion="3"/>
<application android:icon="@drawable/icon" android:label="testSingleTask">
<activity android:name=".ActivityA"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ActivityB"/>
</application>
</manifest>
ActivityA.java:
public class ActivityA extends Activity implements View.OnClickListener
{
@Override
public void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
setContentView( R.layout.main );
View button = findViewById( R.id.tacos );
button.setOnClickListener( this );
}
public void onClick( View view )
{
//Intent i = new Intent( this, ActivityB.class );
Intent i = new Intent();
i.setComponent( new ComponentName( this, ActivityB.class ) );
startActivity( i );
}
}
ActivityB.java:
public class ActivityB extends Activity
{
@Override
public void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
setContentView( R.layout.layout_b );
}
}
Я попытался изменить minSdkVersion безрезультатно. Это просто ошибка, по крайней мере, согласно документации, которая гласит следующее:
Как отмечалось выше, никогда не бывает более одного экземпляра действия "singleTask" или "singleInstance", поэтому ожидается, что этот экземпляр будет обрабатывать все новые намерения. Действие "singleInstance" всегда находится на вершине стека (поскольку это единственное действие в задаче), поэтому оно всегда в состоянии обработать намерение. Однако действие "singleTask" может иметь или не иметь другие действия над ним в стеке. Если это так, он не в состоянии обработать намерение, и намерение отбрасывается. (Даже если намерение отброшено, его прибытие привело бы к тому, что задача вышла на передний план, где она и останется.)
Я думаю, что это поведение, которое вы хотите:
singleTask сбрасывает стек на домашней прессе по какой-то отсталой причине, которую я не понимаю. Вместо этого решение состоит в том, чтобы не использовать singleTask и вместо этого использовать стандартную или singleTop для запуска (хотя на сегодняшний день я пробовал только с singleTop).
Поскольку приложения имеют сходство друг с другом, запуск действия, как это:
Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
if(launchIntent!=null) {
launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
}
приведет к тому, что ваш стек активности снова появится, как и прежде, без начала нового действия со старым (что раньше было моей главной проблемой). Флаги являются важными:
FLAG_ACTIVITY_NEW_TASK Добавлен на уровне API 1
Если установлено, это действие станет началом новой задачи в этом стеке истории. Задача (от действия, которое ее запустило, до следующего действия задачи) определяет элементарную группу действий, в которую пользователь может перейти. Задачи могут быть перемещены на передний план и фон; все действия внутри определенной задачи всегда остаются в том же порядке. См. Задачи и Back Stack для получения дополнительной информации о задачах.
Этот флаг обычно используется действиями, которые хотят представить поведение стиля "запуска": они предоставляют пользователю список отдельных действий, которые могут быть выполнены, в противном случае они выполняются полностью независимо от запускающего их действия.
При использовании этого флага, если задача уже запущена для действия, которое вы сейчас запускаете, то новое действие не будет запущено; вместо этого текущая задача будет просто перенесена в переднюю часть экрана с состоянием, в котором она была последней. Смотрите флаг FLAG_ACTIVITY_MULTIPLE_TASK, чтобы отключить это поведение.
Этот флаг нельзя использовать, когда вызывающая сторона запрашивает результат от запускаемого действия.
А также:
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED Добавлено на уровне API 1
Если установлено, и это действие либо запускается в новой задаче, либо выводится на вершину существующей задачи, то оно будет запущено как входная дверь задачи. Это приведет к применению любых сходств, необходимых для того, чтобы эта задача находилась в надлежащем состоянии (либо перемещение действий к ней), либо просто сброс ее в исходное состояние при необходимости.
Без них запущенная активность будет просто перенесена поверх старого стека или другого нежелательного поведения (в этом случае, конечно)
Я полагаю, что проблема с не получением последнего Намерения может быть решена следующим образом (из моей головы):
@Override
public void onActivityReenter (int resultCode, Intent data) {
onNewIntent(data);
}
Попробуйте!
Я обнаружил, что эта проблема возникает, только если режим запуска активности запуска установлен на singleTask или singleInstance.
Итак, я создал новую активность запуска, режим запуска которой является стандартным или singleTop. И сделал это действие пусковой установки, чтобы вызвать мое старое основное действие, режим запуска которого является одиночной задачей.
LauncherActivity (standard/no history) -> MainActivity (singleTask).
Установите заставку для активности пусковой установки. И убил активность лаунчера сразу после вызова основного действия.
public LauncherActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, HomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_TASK_ON_HOME);
startActivity(intent);
finish();
}
}
<activity
android:name=".LauncherActivity"
android:noHistory="true"
android:theme="@style/Theme.LauncherScreen">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Launcher screen theme should be set for the case that app is restarting after the process is killed. -->
<activity
android:name=".MainActivity"
android:launchMode="singleTask"
android:theme="@style/Theme.LauncherScreen"/>
Плюсы: можно сохранить режим запуска MainActivity как singleTask, чтобы всегда было не более одной MainActivity.
Если оба A и B принадлежат одному и тому же Application
попробуйте удалить
android:launchMode="singleTask"
от твоего Activities
и протестируйте, потому что я думаю, что поведение по умолчанию - то, что вы описали как ожидалось.
Вот как я наконец-то решил это странное поведение. В AndroidManifest это то, что я добавил:
Application & Root activity
android:launchMode="singleTop"
android:alwaysRetainTaskState="true"
android:taskAffinity="<name of package>"
Child Activity
android:parentActivityName=".<name of parent activity>"
android:taskAffinity="<name of package>"
Я застрял в этой проблеме, нашел решение: удалить режим запуска из манифеста, добавить эти флаги при каждом запуске действия.Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
это сработало для меня
Всякий раз, когда вы нажимаете кнопку "Домой", чтобы вернуться на домашний экран, стек действий убивает некоторые из ранее запущенных и запущенных приложений.
Чтобы убедиться в этом, попробуйте запустить приложение из панели уведомлений после перехода от A к B в вашем приложении и вернуться с помощью кнопки "назад".......... вы найдете ваше приложение в том же состоянии, что и вы Оставь это.
В дочерней активности или в активности B
@Override
public void onBackPressed() {
Intent intent = new Intent(getApplicationContext(), Parent.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
finish();
}
При использовании режима запуска в качестве singleTop обязательно вызовите метод finish() (для текущей операции, скажем, A) при запуске следующей операции (с помощью метода startActivity(Intent), скажем, B). Таким образом, текущая активность уничтожается. A -> B -> Приостановить приложение и щелкнуть по значку запуска, запускает A При создании метода A вам нужно проверить,
if(!TaskRoot()) {
finish();
return;
}
Таким образом, при запуске приложения мы проверяем корневую задачу, и ранее корневая задача была B, но не A. Таким образом, эта проверка уничтожает действие A и переносит нас к действию B, которое в настоящее время находится на вершине стека. Надеюсь, это работает для вас!
Добавьте ниже в активность манифеста Android, это добавит новую задачу в верхней части представления, уничтожая предыдущие задачи. android:launchMode="singleTop", как показано ниже
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:launchMode="singleTop"
android:theme="@style/AppTheme.NoActionBar">
</activity>
<activity android:name=".MainActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
// Попытайтесь использовать launchMode="singleTop" в своей основной деятельности для поддержки одного экземпляра вашего приложения. Идите, чтобы проявить и изменить.