woshidan's blog

あいとゆうきとITと、とっておきの話。

Activityの起動とlaunchModeについて

ActivityのlaunchMode の違いが気になっていたので、公式で推奨されている standard, singleTop についてざっくり整理しました。

launchMode=“standard"、あるいは指定がない場合

Androidのシステムは常に、ターゲットタスク*1内で新しいActivityのインスタンスを作成します。

つまり、

<activity android:name=".MainActivity"
    android:launchMode="standard"
    >
    <intent-filter> <!-- IntentFilterの部分は本記事の趣旨には関係ない -->
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

となっている場合、

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainActivity.this, MainActivity.class));
            }
        });
    }

と書いたらボタンを押せば何枚でもMainActivityが生成されてスタックに積まれていくわけです。

こんな感じに。

f:id:woshidan:20170901010221g:plain

launchMode=“singleTop"の場合

Androidのシステムは、ターゲットタスク内で

  • 起動しようとしているActiivtyがタスクのトップにある場合は新しいActivityを生成しない
  • 起動しようとしているActiivtyがタスクのトップにない場合は新しいActivityを生成する

というふるまいをします。ややこしいですね。

ちょっと動かしてみましょう。

<activity
    android:name=".MainActivity"
    android:label="FirstActivity"
    android:launchMode="singleTop">
    <intent-filter> <!-- IntentFilterの部分は本記事の趣旨には関係ない -->
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<activity android:name=".NextActivity"
    android:launchMode="singleTop"
    android:label="SecondActivity"
    >
</activity>
// NextActivityもほぼ同じコード
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.button_launch_first_activity).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainActivity.this, MainActivity.class));
            }
        });

        findViewById(R.id.button_launch_second_activity).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainActivity.this, NextActivity.class));
            }
        });
    }
}

交互に二つのActivityを起動する場合は、新しいActivityが生成されスタックに積まれていきます。

f:id:woshidan:20170901010056g:plain

同じActivityを連続して起動しようとすると、singleTopの 起動しようとしているActiivtyがタスクのトップにある場合は新しいActivityを生成しない という性質により、新しいActivityのインスタンスは生成されず、スタックにあるActivityは増えません。

f:id:woshidan:20170901010153g:plain

ところで、launchModestandard のアクティビティを利用している場合、前のActivityから渡されたIntentをonCreateで受け取って表示やAPIへのリクエストパラメータに利用します。

それでは、singleTopのActiivtyがスタックの一番上にあるときに、もう一度Activityを起動しようとしたことを検知、あるいは、その際渡されたIntentを元に表示などを変更したい場合、どうしたらいいでしょうか。

公式ドキュメントにも書いてありますが、Activity.onNewIntent(Intent)で可能です。

// 先ほどのMainActivityのコードを少し編集
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.button_launch_first_activity).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, MainActivity.class);
                intent.putExtra("KEY_MESSAGE", "sent new intent");
                startActivity(intent);
            }
        });

        // 略
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        
        Toast.makeText(this, intent.getStringExtra("KEY_MESSAGE"), Toast.LENGTH_SHORT).show();
    }

f:id:woshidan:20170901010031g:plain

ECやメディア系のアプリで起動時にダイアローグを表示して、押されたボタンによっては、トップ画面の表示をデフォルトではなく特定の単語やカテゴリで検索かけた結果を表示するようにしたい、なんていう場合などは覚えておくと便利そうです。

現場からは以上です。

*1:おおむね起動しようとしているActivityの属するアプリのタスク