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が生成されてスタックに積まれていくわけです。
こんな感じに。
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が生成されスタックに積まれていきます。
同じActivityを連続して起動しようとすると、singleTopの 起動しようとしているActiivtyがタスクのトップにある場合は新しいActivityを生成しない
という性質により、新しいActivityのインスタンスは生成されず、スタックにあるActivityは増えません。
ところで、launchMode
が standard
のアクティビティを利用している場合、前の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(); }
ECやメディア系のアプリで起動時にダイアローグを表示して、押されたボタンによっては、トップ画面の表示をデフォルトではなく特定の単語やカテゴリで検索かけた結果を表示するようにしたい、なんていう場合などは覚えておくと便利そうです。
現場からは以上です。
*1:おおむね起動しようとしているActivityの属するアプリのタスク