Android:launchMode=“singleTask”中的错误? ->活动堆栈未保留

发布于 2024-08-24 09:35:08 字数 612 浏览 6 评论 0原文

我的主要活动 A 在清单中设置了 android:launchMode="singleTask" 。现在,每当我从那里开始另一个活动时,例如 B 并按手机上的 HOME BUTTON 返回主屏幕,然后再次返回我的应用程序,或者通过按应用程序的按钮或长按HOME BUTTON来显示我最近的应用程序,它不会保留我的活动堆栈并直接返回A而不是预期的活动<代码>B。

这里有两种行为:

Expected: A > B > HOME > B
Actual: A > B > HOME > A (bad!)

我是否缺少某个设置或者这是一个错误?如果是后者,在错误修复之前是否有解决方法?

仅供参考:此问题已在此处讨论过。然而,目前似乎还没有任何真正的解决方案。

My main activity A has as set android:launchMode="singleTask" in the manifest. Now, whenever I start another activity from there, e.g. B and press the HOME BUTTON on the phone to return to the home screen and then again go back to my app, either via pressing the app's button or pressing the HOME BUTTONlong to show my most recent apps it doesn't preserve my activity stack and returns straight to A instead of the expected activity B.

Here the two behaviors:

Expected: A > B > HOME > B
Actual: A > B > HOME > A (bad!)

Is there a setting I'm missing or is this a bug? If the latter, is there a workaround for this until the bug is fixed?

FYI: This question has already been discussed here. However, it doesn't seem that there is any real solution to this, yet.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

送君千里 2024-08-31 09:35:08

这不是一个错误。当启动现有的 singleTask 活动时,堆栈中位于该活动之上的所有其他活动都将被销毁。

当您按 HOME 并再次启动 Activity 时,ActivityManger 会调用 Intent

{act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER]flag=FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_RESET_IF_NEEDED cmp=A}

因此结果是 A > B>首页> A.

当A的launchMode为“Standard”时,情况有所不同。包含A的任务将来到前台并保持与之前相同的状态。

您可以创建一个“标准”活动,例如。 C 作为启动器并在 C 的 onCreate 方法中启动Activity(A)

OR

只需删除 launchMode="singleTask" 并设置 FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOP每当向 A 调用意图时标记

This is not a bug. When an existing singleTask activity is launched, all other activities above it in the stack will be destroyed.

When you press HOME and launch the activity again, ActivityManger calls an intent

{act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER]flag=FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_RESET_IF_NEEDED cmp=A}

So the result is A > B > HOME > A.

It's different when A's launchMode is "Standard". The task which contains A will come to the foreground and keep the state the same as before.

You can create a "Standard" activity eg. C as the launcher and startActivity(A) in the onCreate method of C

OR

Just remove the launchMode="singleTask" and set FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOP flag whenever call an intent to A

小猫一只 2024-08-31 09:35:08

来自 http://developer.android.com/guide/topics/manifest singleTask 上的 /activity-element.html

系统在新任务的根部创建活动并将意图路由到它。但是,如果该 Activity 的实例已存在,系统将通过调用其 onNewIntent() 方法将 Intent 路由到现有实例,而不是创建一个新实例。

这意味着当action.MAIN和category.LAUNCHER标志从启动器瞄准您的应用程序时,系统宁愿将意图路由到现有的ActivityA,而不是创建新任务并将新的ActivityA设置为根。它宁愿拆除 ActivityA 所在的现有任务之上的所有活动,并调用它的 onNewIntent()。

如果您想捕获 singleTop 和 singleTask 的行为,请使用 singleTask launchMode 创建一个名为 SingleTaskActivity 的单独“委托”活动,该活动只需在其 onCreate() 中调用 singleTop 活动,然后自行完成。 singleTop 活动仍将具有 MAIN/LAUNCHER 意图过滤器以继续充当应用程序的主 Launcher 活动,但是当其他活动希望调用此 singleTop 活动时,它必须调用 SingleTaskActivity 以保留 singleTask 行为。传递给 singleTask 活动的意图也应该传递给 singleTop Activity,因此,由于我想要同时拥有 singleTask 和 singleTop 启动模式,因此类似以下内容对我有用。

<activity android:name=".activities.SingleTaskActivity"
              android:launchMode="singleTask"
              android:noHistory="true"/>

public class SingleTaskActivity extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = getIntent();
        intent.setClass(this, SingleTop.class);
        startActivity(intent);
    }
}

并且您的 singleTop 活动将继续具有其 singleTop 启动模式。

    <activity
        android:name=".activities.SingleTopActivity"
        android:launchMode="singleTop"
        android:noHistory="true"/>

祝你好运。

From http://developer.android.com/guide/topics/manifest/activity-element.html on singleTask

The system creates the activity at the root of a new task and routes the intent to it. However, if an instance of the activity already exists, the system routes the intent to existing instance through a call to its onNewIntent() method, rather than creating a new one.

This means when the action.MAIN and category.LAUNCHER flags targets your application from the Launcher, the system would rather route the intent to the existing ActivityA as opposed to creating a new task and setting a new ActivityA as the root. It would rather tear down all activities above existing task ActivityA lives in, and invoke it's onNewIntent().

If you want to capture both the behavior of singleTop and singleTask, create a separate "delegate" activity named SingleTaskActivity with the singleTask launchMode which simply invokes the singleTop activity in its onCreate() and then finishes itself. The singleTop activity would still have the MAIN/LAUNCHER intent-filters to continue acting as the application's main Launcher activity, but when other activities desire calling this singleTop activity it must instead invoke the SingleTaskActivity as to preserve the singleTask behavior. The intent being passed to the singleTask activity should also be carried over to the singleTop Activity, so something like the following has worked for me since I wanted to have both singleTask and singleTop launch modes.

<activity android:name=".activities.SingleTaskActivity"
              android:launchMode="singleTask"
              android:noHistory="true"/>

public class SingleTaskActivity extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = getIntent();
        intent.setClass(this, SingleTop.class);
        startActivity(intent);
    }
}

And your singleTop activity would continue having its singleTop launch mode.

    <activity
        android:name=".activities.SingleTopActivity"
        android:launchMode="singleTop"
        android:noHistory="true"/>

Good luck.

陪我终i 2024-08-31 09:35:08

Stefan,你找到这个问题的答案了吗?我为此编写了一个测试用例,并且看到了相同的(令人困惑的)行为...我将粘贴下面的代码,以防有人出现并看到一些明显的东西:

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”活动在堆栈中可能有也可能没有位于其上方的其他活动。如果是,则它无法处理该意图,并且该意图将被丢弃。 (即使意图被放弃,它的到来也会导致任务到达前台,并保留在那里。)

Stefan, you ever find an answer to this? I put together a testcase for this and am seeing the same (perplexing) behavior...I'll paste the code below in case anyone comes along and sees something obvious:

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

I tried changing minSdkVersion to no avail. This just seems to be a bug, at least according to the documentation, which states the following:

As noted above, there's never more than one instance of a "singleTask" or "singleInstance" activity, so that instance is expected to handle all new intents. A "singleInstance" activity is always at the top of the stack (since it is the only activity in the task), so it is always in position to handle the intent. However, a "singleTask" activity may or may not have other activities above it in the stack. If it does, it is not in position to handle the intent, and the intent is dropped. (Even though the intent is dropped, its arrival would have caused the task to come to the foreground, where it would remain.)

深海蓝天 2024-08-31 09:35:08

我认为这就是您想要的行为:

由于某些我不明白的迟钝原因,singleTask 在主屏幕上重置堆栈。
解决方案是不使用 singleTask 并使用 standardsingleTop 来代替启动器活动(尽管迄今为止我只尝试过 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

如果设置,此活动将成为此上新任务的开始
历史堆栈。任务(从启动它的活动到下一个任务)
任务活动)定义用户可以执行的原子活动组
移动到.任务可以移到前台和后台;所有的
特定任务内的活动始终保持不变
命令。有关任务的更多信息,请参阅任务和返回堆栈。

此标志通常由想要呈现的活动使用
“启动器”风格的行为:它们为用户提供了一个单独的列表
可以完成的事情,否则完全独立运行
启动它们的活动。

使用此标志时,如果该活动的任务已在运行
您现在正在开始,那么新的活动将不会开始;
相反,当前任务将被简单地带到前面
屏幕及其上次所处的状态。请参阅 FLAG_ACTIVITY_MULTIPLE_TASK
用于禁用此行为的标志。

当调用者请求结果时,不能使用此标志
活动正在启动。

和:

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 在 API 级别 1 中添加

如果设置,并且此活动要么在新任务中启动,要么
将现有任务置于顶部,然后它将作为
任务的前门。这将导致应用任何
使该任务处于适当状态所需的亲和力(无论是移动
活动),或简单地将该任务重置为其
如果需要的话初始状态。

如果没有它们,启动的活动只会被推到旧堆栈的顶部或其他一些不良行为(当然在这种情况下)

我相信没有收到最新意图的问题可以这样解决(在我的脑海中):

@Override
public void onActivityReenter (int resultCode, Intent data) {
    onNewIntent(data);
}

尝试一下出去!

I think this is the behaviour you want:

singleTask resets the stack on home press for some retarded reason that I don't understand.
The solution is instead to not use singleTask and use standard or singleTop for launcher activity instead (I've only tried with singleTop to date though).

Because apps have an affinity for each other, launching an activity like this:

Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
if(launchIntent!=null) {
    launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
}

will cause your activty stack to reappear as it was, without it starting a new activity upon the old one (which was my main problem before). The flags are the important ones:

FLAG_ACTIVITY_NEW_TASK Added in API level 1

If set, this activity will become the start of a new task on this
history stack. A task (from the activity that started it to the next
task activity) defines an atomic group of activities that the user can
move to. Tasks can be moved to the foreground and background; all of
the activities inside of a particular task always remain in the same
order. See Tasks and Back Stack for more information about tasks.

This flag is generally used by activities that want to present a
"launcher" style behavior: they give the user a list of separate
things that can be done, which otherwise run completely independently
of the activity launching them.

When using this flag, if a task is already running for the activity
you are now starting, then a new activity will not be started;
instead, the current task will simply be brought to the front of the
screen with the state it was last in. See FLAG_ACTIVITY_MULTIPLE_TASK
for a flag to disable this behavior.

This flag can not be used when the caller is requesting a result from
the activity being launched.

And:

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED Added in API level 1

If set, and this activity is either being started in a new task or
bringing to the top an existing task, then it will be launched as the
front door of the task. This will result in the application of any
affinities needed to have that task in the proper state (either moving
activities to or from it), or simply resetting that task to its
initial state if needed.

Without them the launched activity will just be pushed ontop of the old stack or some other undesirable behaviour (in this case of course)

I believe the problem with not receiving the latest Intent can be solved like this (out of my head):

@Override
public void onActivityReenter (int resultCode, Intent data) {
    onNewIntent(data);
}

Try it out!

墨洒年华 2024-08-31 09:35:08

我发现仅当启动器活动的启动模式设置为 singleTask 或 singleInstance 时才会出现此问题。

因此,我创建了一个新的启动器 Activity,其启动模式为 standard 或 singleTop。并使这个启动器活动调用我的旧主要活动,其启动模式是单任务。

LauncherActivity(标准/无历史记录)-> 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。

I've found this issue happens only if the launcher activity's launch mode is set to singleTask or singleInstance.

So, I've created a new launcher activity whose launch mode is standard or singleTop. And made this launcher activity to call my old main activity whose launch mode is single task.

LauncherActivity (standard/no history) -> MainActivity (singleTask).

Set splash screen to launcher activity. And killed launcher activity right after I call the main activity.

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"/>

Pros: Can keep the MainActivity's launch mode as singleTask to make sure there always is no more than one MainActivity.

此岸叶落 2024-08-31 09:35:08

如果 A 和 B 属于同一个 Application,请尝试

android:launchMode="singleTask"

从您的 Activities 中删除并进行测试,因为我认为默认行为是您所描述的预期行为。

If both A and B belong to the same Application, try removing

android:launchMode="singleTask"

from your Activities and test because I think the default behavior is what you described as expected.

瞄了个咪的 2024-08-31 09:35:08

每当您按下主页按钮返回主屏幕时,活动堆栈都会杀死一些先前启动和正在运行的应用程序。

要验证这一事实,请尝试在应用程序中从 A 转到 B 后从通知面板启动应用程序,然后使用后退按钮返回......您会发现您的应用程序处于与您相同的状态留下了它。

Whenever you press the home button to go back to your home screen the activity stack kills some of the previously launched and running apps.

To verify this fact try to launch an app from the notification panel after going from A to B in your app and come back using the back button ..........you will find your app in the same state as you left it.

墨离汐 2024-08-31 09:35:08

当使用 singleTop 启动模式时,请确保在启动下一个活动(使用 startActivity(Intent) 方法,如 B)时调用 finish()(在当前活动上,如 A)。这样当前的活动就会被破坏。
A-> B->暂停应用程序并单击启动器图标“Starts A”
在 A 的 oncreate 方法中,您需要进行检查,

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

这样在启动应用程序时我们将检查根任务,之前的根任务是 B 而不是 A。因此,此检查会销毁活动 A 并将我们带到当前的活动 B堆栈顶部。
希望它对你有用!

When using launch mode as singleTop make sure to call finish() (on current activity say A) when starting the next activity (using startActivity(Intent) method say B). This way the current activity gets destroyed.
A -> B -> Pause the app and click on launcher Icon, Starts A
In oncreate method of A, you need to have a check,

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

This way when launching app we are checking for root task and previously root task is B but not A. So this check destroys the activity A and takes us to activity B which is currently top of the stack.
Hope it works for you!.

冧九 2024-08-31 09:35:08

这就是我最终解决这个奇怪行为的方法。在 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>"

This is how I finally solved this weird behavior. In AndroidManifest, this is what I added:

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>"
在风中等你 2024-08-31 09:35:08

在 android 清单活动中添加以下内容,它将添加新任务到视图顶部,从而销毁之前的任务。
android:launchMode="singleTop" 如下

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

Add below in android manifest activity, it will add new task to top of the view destroying earlier tasks.
android:launchMode="singleTop" as below

 <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:launchMode="singleTop"
        android:theme="@style/AppTheme.NoActionBar">
    </activity>
橘亓 2024-08-31 09:35:08

在子活动或 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();
    

}

In child activity or in B activity

@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();
    

}
顾铮苏瑾 2024-08-31 09:35:08

我被这个问题困住了,找到了解决方案
从清单中删除 launchMode
每当启动 Activity Intent.FLAG_ACTIVITY_CLEAR_TOP 或 Intent.FLAG_ACTIVITY_SINGLE_TOP 时添加这些标志
这对我有用

I was stuck on the issue figured out a solution
remove launchMode from manifest
add these flags whenever launching the activity Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
this worked for me

是你 2024-08-31 09:35:08
        <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" 来维护应用程序的单个实例。去体现并改变。

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

//Try to use launchMode="singleTop" in your main activity to maintain single instance of your application. Go to manifest and change.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文