web-dev-qa-db-ja.com

Android:launchMode = "singleTask"のバグ? ->アクティビティスタックが保持されない

私の主なアクティビティAのセットはAndroid:launchMode="singleTask"マニフェスト内。今、そこから別のアクティビティを開始するたびに、例えばBを押してHOME BUTTONを押してホーム画面に戻り、アプリのボタンを押すか、HOME BUTTONlong最新のアプリを表示するために、アクティビティスタックを保持せず、予想されるアクティビティAではなくBに直接戻ります。

ここで2つの動作:

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

欠落している設定はありますか、これはバグですか?後者の場合、バグが修正されるまでこれに対する回避策はありますか?

参考までに、この質問はすでに議論されています here 。ただし、これに対する実際の解決策はまだ存在しないようです。

62
znq

これはバグではありません。既存の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です。

AのlaunchModeが「Standard」の場合は異なります。 Aを含むタスクはフォアグラウンドに移動し、以前と同じ状態を維持します。

「標準」アクティビティを作成できます。ランチャーとしてのCとCのonCreateメソッドのstartActivity(A)

[〜#〜]または[〜#〜]

launchMode="singleTask"と設定FLAG_ACTIVITY_CLEAR_TOP|FLAG_ACTIVITY_SINGLE_TOPインテントをAに呼び出すたびにフラグを立てる

41
renchenyu

から http://developer.Android.com/guide/topics/manifest/activity-element.html on singleTask

システムは、新しいタスクのルートにアクティビティを作成し、インテントをそれにルーティングします。ただし、アクティビティのインスタンスが既に存在する場合、システムは、新しいインスタンスを作成するのではなく、onNewIntent()メソッドの呼び出しを通じてインテントを既存のインスタンスにルーティングします。

つまり、action.MAINおよびcategory.LAUNCHERフラグがLauncherからアプリケーションをターゲットとする場合、システムは、新しいタスクを作成してルートとして新しいActivityAを設定するのではなく、既存のActivityAにインテントをルーティングします。むしろ、ActivityAが存在する既存のタスクより上のすべてのアクティビティを破棄し、onNewIntent()を呼び出します。

SingleTopとsingleTaskの両方の動作をキャプチャする場合は、onTask()でsingleTopアクティビティを呼び出して終了するsingleTask launchModeを使用して、SingleTaskActivityという名前の別の「デリゲート」アクティビティを作成します。 singleTopアクティビティには、アプリケーションのメインLauncherアクティビティとして機能し続けるMAIN/LAUNCHERインテントフィルターがまだありますが、他のアクティビティがこのsingleTopアクティビティを呼び出す必要がある場合は、代わりにsingleTask動作を保持するためにSingleTaskActivityを呼び出す必要があります。 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);
    }
}

また、singleTopアクティビティのsingleTop起動モードは継続されます。

    <activity
        Android:name=".activities.SingleTopActivity"
        Android:launchMode="singleTop"/>

幸運を。

7
Ryan

ステファン、あなたはこれに対する答えを見つけましたか?私はこれのためにテストケースを作成し、同じ(困惑する)動作を見ています...誰かが来て、明らかな何かを見た場合に備えて、以下のコードを貼り付けます:

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を無効に変更してみました。少なくとも documentation によれば、これはバグのようです。

上記のように、「singleTask」または「singleInstance」アクティビティのインスタンスが複数存在することはないため、そのインスタンスはすべての新しいインテントを処理することが期待されます。 「singleInstance」アクティビティは常にスタックの最上部にあるため(タスク内の唯一のアクティビティであるため)、常にインテントを処理する位置にあります。ただし、「singleTask」アクティビティには、スタック内で他のアクティビティがある場合とない場合があります。存在する場合、インテントを処理する位置にないため、インテントは削除されます。 (インテントがドロップされたとしても、その到着によりタスクはフォアグラウンドに到達し、そこに残ります)

5
Eric

私はこれがあなたが望む行動だと思う:

singleTaskは、私が理解していない何らかの遅延した理由で、ホームプレスのスタックをリセットします。代わりに、singleTaskを使用せず、代わりにランチャーアクティビティにstandardまたは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で追加

設定されていて、このアクティビティが新しいタスクで開始されているか、既存のタスクを先頭に持っている場合、タスクの玄関口として起動されます。これにより、そのタスクを適切な状態にする(アクティビティを移動する)ために必要なアフィニティを適用したり、必要に応じてタスクを初期状態にリセットしたりすることができます。

それらがなければ、起動されたアクティビティは古いスタックまたは他の望ましくない動作の上にプッシュされるだけです(この場合はもちろん)

最新のインテントを受け取れないという問題は、次のように解決できると思います(頭から):

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

やってみよう!

3
JohnyTex

AとBの両方が同じApplicationに属している場合、削除してみてください

Android:launchMode="singleTask"

あなたのActivitiesとテストから、私はデフォルトの振る舞いがあなたが期待通りに記述したものだと思うので。

1

これが私がこの奇妙な振る舞いを最終的に解決した方法です。 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>"
0
Carlos Hoyos

以下にAndroidマニフェストアクティビティを追加すると、ビューの先頭に新しいタスクが追加され、以前のタスクが破壊されます。Android:launchMode = "singleTop" as as below

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

起動モードをsingleTopとして使用する場合、次のアクティビティを開始するとき(startActivity(Intent)メソッドを使用してBなど)、必ずfinish()(現在のアクティビティでA)を呼び出してください。これにより、現在のアクティビティが破棄されます。 A-> B->アプリを一時停止してランチャーアイコンをクリックし、Aを開始しますAのoncreateメソッドで、チェックが必要です。

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

この方法でアプリを起動すると、ルートタスクをチェックし、以前はルートタスクはBであるがAではありません。したがって、このチェックはアクティビティAを破棄し、現在スタックの最上位にあるアクティビティBに移動します。それがあなたに役立つことを願っています!.

0
Venkat Saladi
        <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"を使用して、アプリケーションの単一インスタンスを維持してみてください。マニフェストに移動して変更します。

0
Jay

ホームボタンを押してホーム画面に戻るたびに、アクティビティスタックは以前に起動して実行中のアプリの一部を強制終了します。

この事実を確認するには、アプリでAからBに移動した後、通知パネルからアプリを起動し、戻るボタンを使用して戻ってみてください。それを残しました。

0
Siddhartho