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!)

Есть ли параметр, который мне не хватает, или это ошибка? Если последний, есть ли обходной путь для этого, пока ошибка не будет исправлена?

FYI: Этот вопрос уже обсуждался здесь. Однако, похоже, что нет реального решения этого вопроса.

Ответ 1

Это не ошибка. Когда запущено существующее действие 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.

Он отличается, когда "StartMode" является "стандартным". Задача, содержащая A, выйдет на передний план и сохранит состояние так же, как и раньше.

Вы можете создать "стандартную" активность, например. C как средство запуска и startActivity (A) в методе onCreate для C

ИЛИ

Просто удалите launchMode="singleTask" и установите флаг FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOP, когда вы вызываете намерение A

Ответ 2

От http://developer.android.com/guide/topics/manifest/activity-element.html на singleTask

Система создает действие в корне новой задачи и направляет намерение к ней. Однако, если экземпляр действия уже существует, система направляет намерение к существующему экземпляру посредством вызова его метода onNewIntent() вместо создания нового.

Это означает, что когда флаги action.MAIN и category.LAUNCHER нацелены на ваше приложение из средства запуска, система скорее направит намерение к существующему ActivityA, а не создаст новую задачу и установит новую ActivityA в качестве корневого. Скорее, он снесет все действия над существующей задачей ActivityA, в которой он находится, и вызовет ее на NewIntent().

Если вы хотите зафиксировать как поведение singleTop, так и singleTask, создайте отдельное действие "делегировать" с именем SingleTaskActivity с помощью singleTask launchMode, которое просто вызывает действие singleTop в его onCreate(), а затем завершает себя. Действие singleTop по-прежнему будет иметь фильтры-намерения MAIN/LAUNCHER, чтобы продолжать действовать как основное действие Launcher приложения, но когда другие действия желают вызвать это действие singleTop, оно должно вместо этого вызывать SingleTaskActivity, чтобы сохранить поведение singleTask. Намерение, передаваемое в действие SingleTask, также должно быть перенесено в действие 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"/>

Удачи.

Ответ 3

Стефан, ты когда-нибудь находишь ответ на этот вопрос? Я собрал тестовый файл для этого и вижу то же (запутанное) поведение... Я вставлю код ниже, если кто-нибудь появится и увидит что-то очевидное:

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

Ответ 4

Я думаю, что это ваше поведение:

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);
}

Попробуйте!

Ответ 5

Если оба A и B принадлежат одному и тому же Application, попробуйте удалить

android:launchMode="singleTask"

из вашего Activities и теста, потому что я думаю, что поведение по умолчанию - это то, что вы описали как ожидаемое.

Ответ 6

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

Чтобы убедиться в этом, попробуйте запустить приложение из панели уведомлений после перехода от A к B в вашем приложении и вернуться с помощью кнопки "Назад"... вы найдете свое приложение в том же когда вы его оставили.

Ответ 7

При использовании режима запуска в качестве singleTop не забудьте вызвать finish() (по текущей активности сказать A) при запуске следующего действия (используя метод startActivity (Intent) сказать B). Таким образом, текущая деятельность будет уничтожена. A → B → Приостановите приложение и щелкните значок запуска, запустите A В методе oncreate для A вам необходимо иметь чек,

if(!TaskRoot()) {
    finish();
     return;
  }

Таким образом, при запуске приложения мы проверяем корневую задачу, а предыдущая корневая задача - B, но не A. Таким образом, эта проверка уничтожает действие A и ведет нас к активности B, которая в настоящее время является вершиной стека. Надеюсь, это сработает для вас!

Ответ 8

Вот как я окончательно решил это странное поведение. В 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>"

Ответ 9

        <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" в вашем основном действии, чтобы поддерживать один экземпляр вашего приложения. Пойдите в манифест и измените.

Ответ 10

Добавьте ниже в активности манифеста Android, это добавит новую задачу в верхней части представления, уничтожая предыдущие задачи. android: launchMode = "singleTop", как показано ниже

 <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:launchMode="singleTop"
        android:theme="@style/AppTheme.NoActionBar">
    </activity>