Android:launchMode="singleTask" 中的错误?-> 活动堆栈未保留
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2417468/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Android: bug in launchMode="singleTask"? -> activity stack not preserved
提问by znq
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 BUTTON
long to show my most recent apps it doesn't preserve my activity stack and returns straight to A
instead of the expected activity B
.
我的主要活动A
已android:launchMode="singleTask"
在清单中设置。现在,每当我从那里开始另一个活动时,例如B
按下HOME BUTTON
手机上的 返回主屏幕,然后再次返回我的应用程序,通过按下应用程序的按钮或HOME BUTTON
长按显示我最近的应用程序它不会不保留我的活动堆栈并直接返回A
而不是预期的活动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.
仅供参考:这里已经讨论过这个问题。但是,似乎还没有任何真正的解决方案。
回答by renchenyu
This is not a bug. When an existing singleTask
activity is launched, all other activities above it in the stack will be destroyed.
这不是一个错误。当一个现有的singleTask
Activity 被启动时,堆栈中它上面的所有其他 Activity 都将被销毁。
When you press HOME
and launch the activity again, ActivityManger
calls an intent
当您HOME
再次按下并启动活动时,ActivityManger
调用一个意图
{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.
所以结果是 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.
当 A 的 launchMode 为“Standard”时就不一样了。包含 A 的任务将进入前台并保持与之前相同的状态。
You can create a "Standard" activity eg. C as the launcher and startActivity(A) in the onCreate method of C
您可以创建一个“标准”活动,例如。C作为启动器和C的onCreate方法中的startActivity(A)
OR
或者
Just remove the launchMode="singleTask"
and set FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOP
flag whenever call an intent to A
每当调用意图 A 时,只需删除launchMode="singleTask"
并设置FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOP
标志
回答by Ryan
From http://developer.android.com/guide/topics/manifest/activity-element.htmlon singleTask
从http://developer.android.com/guide/topics/manifest/activity-element.html上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.
系统在新任务的根创建活动并将意图路由到它。但是,如果 Activity 的实例已经存在,系统会通过调用其 onNewIntent() 方法将意图路由到现有实例,而不是创建一个新实例。
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().
这意味着当 action.MAIN 和 category.LAUNCHER 标志从启动器针对您的应用程序时,系统宁愿将意图路由到现有的 ActivityA,而不是创建新任务并将新的 ActivityA 设置为根。它宁愿拆除 ActivityA 所在的现有任务之上的所有活动,并调用它的 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.
如果您想同时捕获 singleTop 和 singleTask 的行为,请使用 singleTask launchMode 创建一个名为 SingleTaskActivity 的单独“委托”活动,该活动仅在其 onCreate() 中调用 singleTop 活动,然后自行完成。singleTop 活动仍然具有 MAIN/LAUNCHER 意图过滤器以继续充当应用程序的主启动器活动,但是当其他活动希望调用此 singleTop 活动时,它必须改为调用 SingleTaskActivity 以保留 singleTask 行为。传递给 singleTask 活动的意图也应该转移到 singleTop Activity,所以像下面这样的东西对我有用,因为我想同时拥有 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);
}
}
And your singleTop activity would continue having its singleTop launch mode.
并且您的 singleTop 活动将继续使用 singleTop 启动模式。
<activity
android:name=".activities.SingleTopActivity"
android:launchMode="singleTop"/>
Good luck.
祝你好运。
回答by Eric
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:
Stefan,你有没有找到这个问题的答案?我为此编写了一个测试用例,并且看到了相同的(令人困惑的)行为......我将粘贴下面的代码,以防有人出现并看到明显的东西:
AndroidManifest.xml:
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:
活动A.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:
活动B.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:
我尝试更改 minSdkVersion 无济于事。这似乎是一个错误,至少根据文档,它指出以下内容:
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.)
如上所述,“singleTask”或“singleInstance”活动的实例永远不会超过一个,因此预计该实例将处理所有新意图。“singleInstance”活动始终位于堆栈顶部(因为它是任务中唯一的活动),因此它始终处于处理意图的位置。但是,“singleTask”活动在堆栈中可能有也可能没有其他活动。如果是,则它无法处理该意图,并且该意图被丢弃。(即使意图被丢弃,它的到来也会导致任务出现在前台,它会留在那里。)
回答by JohnyTex
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 standardor singleTopfor launcher activity instead (I've only tried with singleTop to date though).
由于某种我不明白的迟钝原因,singleTask 重置了主页上的堆栈。相反,解决方案是不使用 singleTask 并使用标准或singleTop来代替启动器活动(尽管到目前为止我只尝试过使用 singleTop )。
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.
FLAG_ACTIVITY_NEW_TASK 在 API 级别 1 中添加
如果设置,此活动将成为此历史堆栈上新任务的开始。任务(从启动它的活动到下一个任务活动)定义了用户可以移动到的活动原子组。任务可以移动到前台和后台;特定任务中的所有活动始终保持相同的顺序。有关任务的更多信息,请参阅任务和返回堆栈。
此标志通常由想要呈现“启动器”样式行为的活动使用:它们为用户提供可以完成的单独事情的列表,否则这些活动完全独立于启动它们的活动运行。
使用此标志时,如果您正在启动的活动的任务已经在运行,则不会启动新的活动;相反,当前任务将简单地以它上次所处的状态带到屏幕的前面。请参阅 FLAG_ACTIVITY_MULTIPLE_TASK 以获取禁用此行为的标志。
当调用者从正在启动的活动中请求结果时,不能使用此标志。
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.
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 在 API 级别 1 中添加
如果设置,并且此活动正在新任务中启动或将现有任务带到顶部,则它将作为任务的前门启动。这将导致应用使该任务处于正确状态所需的任何关联(将活动移入或移出),或者在需要时简单地将该任务重置为其初始状态。
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):
我相信没有收到最新的 Intent 的问题可以这样解决(在我的脑海中):
@Override
public void onActivityReenter (int resultCode, Intent data) {
onNewIntent(data);
}
Try it out!
试试看!
回答by Diego Torres Milano
If both A and B belong to the same Application
, try removing
如果 A 和 B 属于同一个Application
,请尝试删除
android:launchMode="singleTask"
from your Activities
and test because I think the default behavior is what you described as expected.
来自您的Activities
和测试,因为我认为默认行为是您所描述的预期行为。
回答by Siddhartho
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.
要验证这一事实,请尝试在您的应用程序中从 A 转到 B 后从通知面板启动一个应用程序,然后使用后退按钮返回 ..........您会发现您的应用程序处于与您相同的状态留下它。
回答by Venkat Saladi
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,
当使用启动模式作为 singleTop 时,请确保在启动下一个活动(使用 startActivity(Intent) 方法说 B)时调用完成()(在当前活动上说 A)。这样当前的活动就会被破坏。A -> B -> 暂停应用程序并单击启动器图标,启动 A 在 A 的 oncreate 方法中,您需要进行检查,
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!.
这样,当启动应用程序时,我们正在检查根任务,而先前的根任务是 B 而不是 A。因此,此检查会破坏活动 A 并将我们带到当前位于堆栈顶部的活动 B。希望这对你有用!。
回答by Carlos Hoyos
This is how I finally solved this weird behavior. In AndroidManifest, this is what I added:
这就是我最终解决这种奇怪行为的方法。在 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>"
回答by Rockit Rockit
Add below in android manifest activity, it will add new task to top of the view destroying earlier tasks. android:launchMode="singleTop" as below
在 android manifest 活动中添加下面的内容,它将在视图顶部添加新任务,破坏先前的任务。android:launchMode="singleTop" 如下
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:launchMode="singleTop"
android:theme="@style/AppTheme.NoActionBar">
</activity>
回答by The Finest Artist
I've found this issue happens only if the launcher activity's launch mode is set to singleTask or singleInstance.
我发现只有当启动器活动的启动模式设置为 singleTask 或 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).
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.
优点:可以保持 MainActivity 的启动模式为 singleTask,以确保始终不超过一个 MainActivity。