[英]Android: bug in launchMode="singleTask"? -> activity stack not preserved
我的主要活动
A<\/code>在清单中设置了
android:launchMode="singleTask"<\/code> 。
现在,每当我从那里开始另一个活动时,例如
B<\/code>并按下手机上的主屏幕
HOME BUTTON<\/code>以返回主屏幕,然后再次返回我的应用程序,或者通过按下应用程序的按钮或长按
HOME BUTTON<\/code>以显示我的最近的应用程序它不保留我的活动堆栈并直接返回
A<\/code>而不是预期的活动
B<\/code> 。
这里有两种行为:
Expected: A > B > HOME > B
Actual: A > B > HOME > A (bad!)
这不是一个错误。 当启动现有的singleTask
活动时,堆栈中位于它之上的所有其他活动都将被销毁。
当您按HOME
并再次启动ActivityManger
时, 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 > HOME > A。
当 A 的 launchMode 为“Standard”时就不一样了。 包含 A 的任务将进入前台并保持与之前相同的状态。
您可以创建一个“标准”活动,例如。 C作为启动器和C的onCreate方法中的startActivity(A)
或者
只需删除launchMode="singleTask"
并设置launchMode="singleTask"
FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOP
标志,每当调用意图 A
来自http://developer.android.com/guide/topics/manifest/activity-element.html on singleTask
系统在新任务的根创建活动并将意图路由到它。 但是,如果 Activity 的实例已经存在,系统会通过调用其 onNewIntent() 方法将意图路由到现有实例,而不是创建一个新实例。
这意味着当 action.MAIN 和 category.LAUNCHER 标志从启动器针对您的应用程序时,系统宁愿将意图路由到现有的 ActivityA,而不是创建新任务并将新的 ActivityA 设置为根。 它宁愿拆除 ActivityA 所在的现有任务之上的所有活动,并调用它的 onNewIntent()。
如果您想同时捕获 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"
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"/>
祝你好运。
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>
活动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 );
}
}
活动B.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 中添加
如果设置,此活动将成为此历史堆栈上新任务的开始。 任务(从启动它的活动到下一个任务活动)定义了用户可以移动到的活动原子组。 任务可以移动到前台和后台; 特定任务中的所有活动始终保持相同的顺序。 有关任务的更多信息,请参阅任务和返回堆栈。
此标志通常由想要呈现“启动器”样式行为的活动使用:它们为用户提供可以完成的单独事情的列表,否则这些活动完全独立于启动它们的活动运行。
使用此标志时,如果您正在启动的活动的任务已经在运行,则不会启动新的活动; 相反,当前任务将简单地以其上次处于的状态被带到屏幕的前面。请参阅 FLAG_ACTIVITY_MULTIPLE_TASK 以获取禁用此行为的标志。
当调用者从正在启动的活动中请求结果时,不能使用此标志。
和:
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED 在 API 级别 1 中添加
如果设置,并且此活动正在新任务中启动或将现有任务带到顶部,则它将作为任务的前门启动。 这将导致应用使该任务处于正确状态所需的任何关联(将活动移入或移出),或者在需要时简单地将该任务重置为其初始状态。
没有它们,启动的活动将被推到旧堆栈或其他一些不良行为的顶部(当然在这种情况下)
我相信没有收到最新的 Intent 的问题可以这样解决(在我的脑海中):
@Override
public void onActivityReenter (int resultCode, Intent data) {
onNewIntent(data);
}
试试看!
我发现只有当启动器活动的启动模式设置为 singleTask 或 singleInstance 时才会发生此问题。
因此,我创建了一个新的启动器活动,其启动模式为标准或单顶。 并使这个启动器活动调用我的旧主活动,其启动模式为单任务。
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
和测试,因为我认为默认行为是您所描述的预期行为。
每当您按下主页按钮返回主屏幕时,活动堆栈都会杀死一些之前启动和运行的应用程序。
要验证这一事实,请尝试在您的应用程序中从 A 转到 B 后从通知面板启动一个应用程序,然后使用后退按钮返回 ..........您会发现您的应用程序处于与您相同的状态留下它。
当使用启动模式作为 singleTop 时,请确保在启动下一个活动(使用 startActivity(Intent) 方法说 B)时调用完成()(在当前活动上说 A)。 这样当前的活动就会被破坏。 A -> B -> 暂停应用程序并单击启动器图标,启动 A 在 A 的 oncreate 方法中,您需要进行检查,
if(!TaskRoot()) {
finish();
return;
}
这样,当启动应用程序时,我们正在检查根任务,而之前的根任务是 B 而不是 A。因此,此检查会破坏活动 A 并将我们带到当前位于堆栈顶部的活动 B。 希望这对你有用!。
这就是我最终解决这种奇怪行为的方法。 在 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>"
在 android manifest 活动中添加下面的内容,它将在视图顶部添加新任务,破坏先前的任务。 android:launchMode="singleTop" 如下
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:launchMode="singleTop"
android:theme="@style/AppTheme.NoActionBar">
</activity>
在儿童活动或 B 活动中
<\/blockquote>
<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"来维护您的应用程序的单个实例。 去显化和改变。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.