繁体   English   中英

"<i>Android: bug in launchMode=&quot;singleTask&quot;?<\/i> Android:launchMode =“singleTask”中的错误?<\/b> <i>-&gt; activity stack not preserved<\/i> -&gt; 活动堆栈未保留<\/b>"

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM