タイトル: アクティビティ (Android アプリケーション)
SEOタイトル: Android Activity ライフサイクル完全解説 — onCreate/onPause/Intent/Backstack
この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;background:#fffbea;border:1px solid #f4c430;margin:1em 0;width:100%"><tr><td style="background:#f4c430;color:#000;font-weight:bold;padding:8px">この記事の要点</td></tr><tr><td style="padding:10px"><ul><li><strong>Activity</strong> = Android の 1 画面に対応するクラス。<code>AppCompatActivity</code> を継承するのが標準</li><li>ライフサイクル: <code>onCreate</code> → <code>onStart</code> → <code>onResume</code> → (利用中) → <code>onPause</code> → <code>onStop</code> → <code>onDestroy</code></li><li>画面遷移: <code>Intent</code> + <code>startActivity(intent)</code>、データ受け渡しは <code>Bundle</code> / <code>Intent.putExtra</code></li><li>結果返却: <code>startActivityForResult</code> は<strong>廃止</strong>。Activity Result API (<code>registerForActivityResult</code>) を使う</li><li>モダン構成: <strong>Single Activity + Fragment/Compose Navigation</strong> が推奨、launchMode (singleTop / singleTask) で挙動制御</li></ul></td></tr></table>
<h2>Activity とは</h2>
<p><strong>Activity</strong> は Android アプリにおける「1 つの画面」を表す中心クラスです。ユーザーが見る UI の入り口でもあり、ライフサイクル管理・画面遷移・状態保存の起点となります。アプリは複数の Activity から構成され、それぞれが独立したスタックエントリとして遷移します。</p>
<p>近年は<strong>Single Activity 構成(Activity 1 個 + Fragment / Jetpack Compose Navigation)</strong>が主流ですが、Activity の役割を理解しないと深い設計はできません。</p>
<h2>最小の Activity</h2><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-kotlin">class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<Button>(R.id.btn_next).setOnClickListener {
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("user_id", 42)
startActivity(intent)
}
}
}
// AndroidManifest.xml には登録必須
// <activity android:name=".MainActivity" android:exported="true">
// <intent-filter>
// <action android:name="android.intent.action.MAIN" />
// <category android:name="android.intent.category.LAUNCHER" />
// </intent-filter>
// </activity></code></pre>
<h2>ライフサイクル</h2>
<p>Activity は OS の状態に応じて、決まったコールバックが順番に呼ばれます。これがライフサイクルです。</p>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>メソッド</th><th>呼ばれるタイミング</th><th>典型処理</th></tr></thead>
<tbody>
<tr><td><code>onCreate</code></td><td>初回生成(Activity 作成)</td><td>レイアウト設定、初期化</td></tr>
<tr><td><code>onStart</code></td><td>画面が見える直前</td><td>UI 更新の準備</td></tr>
<tr><td><code>onResume</code></td><td>フォアグラウンドで操作可能になる直前</td><td>センサー / カメラ ON</td></tr>
<tr><td><code>onPause</code></td><td>別画面で半分隠れる直前</td><td>センサー / カメラ OFF、軽い保存</td></tr>
<tr><td><code>onStop</code></td><td>完全に見えなくなった</td><td>ヘビーな処理停止、DB に保存</td></tr>
<tr><td><code>onSaveInstanceState</code></td><td>onPause/onStop 直前</td><td>状態を Bundle に保存(画面回転対策)</td></tr>
<tr><td><code>onRestart</code></td><td>onStop → 復帰</td><td>必要に応じて再開</td></tr>
<tr><td><code>onDestroy</code></td><td>Activity 破棄前</td><td>リスナー解除、リソース解放</td></tr>
</tbody>
</table><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-kotlin">class DetailActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_detail)
val userId = intent.getIntExtra("user_id", -1)
Log.d("Lifecycle", "onCreate userId=$userId")
}
override fun onStart() { super.onStart(); Log.d("Lifecycle", "onStart") }
override fun onResume() { super.onResume(); Log.d("Lifecycle", "onResume") }
override fun onPause() { super.onPause(); Log.d("Lifecycle", "onPause") }
override fun onStop() { super.onStop(); Log.d("Lifecycle", "onStop") }
override fun onDestroy() { super.onDestroy(); Log.d("Lifecycle", "onDestroy") }
override fun onSaveInstanceState(out: Bundle) {
out.putString("draft", editText.text.toString())
super.onSaveInstanceState(out)
}
}</code></pre>
<h2>Intent と Bundle でデータ受け渡し</h2><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-kotlin">// 送信側
val intent = Intent(this, DetailActivity::class.java).apply {
putExtra("user_id", 42)
putExtra("name", "Taro")
putExtra("is_admin", true)
}
startActivity(intent)
// 受信側 (DetailActivity.onCreate)
val userId = intent.getIntExtra("user_id", -1)
val name = intent.getStringExtra("name") ?: "Unknown"
val isAdmin = intent.getBooleanExtra("is_admin", false)
// Parcelable / Serializable も渡せるが、Activity 間で大量データはやめる
// 推奨は ViewModel 共有 (Single Activity 構成) or DB / SharedPreferences 経由</code></pre>
<h2>結果を受け取る (Activity Result API)</h2>
<p><code>startActivityForResult</code> は API レベル 31 で<strong>廃止</strong>。代替として <strong>Activity Result API</strong> を使います。</p><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-kotlin">// 旧 (廃止)
// startActivityForResult(intent, REQUEST_CODE_PICK)
// → onActivityResult(requestCode, resultCode, data)
// 新: registerForActivityResult
class MainActivity : AppCompatActivity() {
private val pickContactLauncher = registerForActivityResult(
ActivityResultContracts.PickContact()
) { uri: Uri? ->
uri?.let { Log.d("Pick", "Selected $it") }
}
private val cameraLauncher = registerForActivityResult(
ActivityResultContracts.TakePicturePreview()
) { bitmap: Bitmap? ->
bitmap?.let { imageView.setImageBitmap(it) }
}
fun openPicker() {
pickContactLauncher.launch(null)
}
fun openCamera() {
cameraLauncher.launch(null)
}
}</code></pre>
<h2>Tasks と Back Stack</h2>
<p>Activity は<strong>タスク (Task)</strong> という後入れ先出しスタックに積まれます。「戻る」ボタンでスタックから pop されます。</p>
<table border="1" cellpadding="6" cellspacing="0" style="border-collapse:collapse;width:100%">
<thead><tr style="background:#e3f2fd"><th>launchMode</th><th>挙動</th><th>用途</th></tr></thead>
<tbody>
<tr><td><code>standard</code> (デフォルト)</td><td>毎回新規生成しスタックに積む</td><td>通常画面</td></tr>
<tr><td><code>singleTop</code></td><td>スタック頂上が同一なら新規生成しない(onNewIntent)</td><td>通知 → 既存画面</td></tr>
<tr><td><code>singleTask</code></td><td>タスク内で 1 つ。既存 Instance に戻り、上に積まれたものを破棄</td><td>メイン / ホーム画面</td></tr>
<tr><td><code>singleInstance</code></td><td>専用タスク。常に 1 個</td><td>通話 / アラーム画面</td></tr>
</tbody>
</table><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-xml"><!-- AndroidManifest.xml -->
<activity
android:name=".MainActivity"
android:launchMode="singleTop" />
<!-- 通知タップ → 既存 MainActivity を再利用、onNewIntent が呼ばれる --></code></pre>
<h2>Jetpack Compose / Fragment との関係</h2>
<p>モダンな構成では Activity を多用しません。</p>
<ul>
<li><strong>Single Activity + Fragment + Navigation Component</strong> — 画面遷移を Fragment に委譲</li>
<li><strong>Single Activity + Jetpack Compose + Navigation</strong> — 画面遷移を Composable に委譲(最新主流)</li>
<li><strong>複数 Activity</strong> — レガシー or プロセス分離が必要な場合のみ</li>
</ul><pre style="background:#1e293b;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;line-height:1.5"><code class="language-kotlin">// Compose Navigation で「画面遷移」を Activity 無しで実現
@Composable
fun AppNav() {
val nav = rememberNavController()
NavHost(nav, startDestination = "list") {
composable("list") { ListScreen(onClick = { id -> nav.navigate("detail/$id") }) }
composable("detail/{id}") { backStackEntry ->
DetailScreen(id = backStackEntry.arguments?.getString("id"))
}
}
}</code></pre>
<h2>FAQ</h2>
<p><strong>Q: 画面回転でデータが消える</strong><br />
A: <code>onSaveInstanceState</code> で Bundle に保存、または <strong>ViewModel</strong> に持たせる(Configuration Change を超えて生存)。</p>
<p><strong>Q: <code>finish()</code> と <code>onBackPressed()</code> の違い</strong><br />
A: finish は明示的に閉じる(戻るボタンを押した相当)、onBackPressed は戻るボタン押下時のフック。OnBackPressedDispatcher を使うのが推奨。</p>
<p><strong>Q: Single Activity 構成のメリット</strong><br />
A: 画面間のデータ共有が容易(ViewModel スコープ)、Animations が滑らか、メモリ効率良好、Compose との相性◎。</p>