1.

Android Activity 間パラメータ受け渡し完全ガイド

編集
この記事の要点
  • Activity 間の値渡し: 基本は Intent.putExtra()getIntent().getStringExtra()
  • プリミティブ以外(オブジェクト)は Parcelable(推奨)or Serializable
  • Kotlin は @Parcelize アノテーションでボイラープレート激減
  • モダン Android は Navigation Component + Safe Args で型安全な受渡
  • ViewModel + SavedStateHandle でプロセス kill にも耐える状態保持
  • 永続化が必要なら SharedPreferences / DataStore / Room
  • startActivityForResult は非推奨registerForActivityResult(ActivityResultContracts...)

基本: Intent.putExtra

Android で Activity 間に値を渡すもっとも基本の方法。Intent に extras(キー値ペア)として詰めて起動先で取り出します。

Java

// 送信側
Intent intent = new Intent(this, DetailActivity.class);
intent.putExtra("USER_ID", 42);
intent.putExtra("USER_NAME", "Yamada");
intent.putExtra("IS_VIP", true);
startActivity(intent);

// 受信側 (DetailActivity)
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Intent intent = getIntent();
    int userId   = intent.getIntExtra("USER_ID", -1);
    String name  = intent.getStringExtra("USER_NAME");
    boolean vip  = intent.getBooleanExtra("IS_VIP", false);
}

Kotlin

// 送信側
val intent = Intent(this, DetailActivity::class.java).apply {
    putExtra("USER_ID", 42)
    putExtra("USER_NAME", "Yamada")
    putExtra("IS_VIP", true)
}
startActivity(intent)

// 受信側
val userId = intent.getIntExtra("USER_ID", -1)
val name   = intent.getStringExtra("USER_NAME")
val vip    = intent.getBooleanExtra("IS_VIP", false)

定数化(推奨)

class DetailActivity : AppCompatActivity() {
    companion object {
        private const val EXTRA_USER_ID = "USER_ID"
        private const val EXTRA_USER_NAME = "USER_NAME"

        fun newIntent(ctx: Context, userId: Int, name: String): Intent =
            Intent(ctx, DetailActivity::class.java).apply {
                putExtra(EXTRA_USER_ID, userId)
                putExtra(EXTRA_USER_NAME, name)
            }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val userId = intent.getIntExtra(EXTRA_USER_ID, -1)
        val name   = intent.getStringExtra(EXTRA_USER_NAME) ?: ""
    }
}

// 呼び出し
startActivity(DetailActivity.newIntent(this, 42, "Yamada"))

キー名のタイポ防止 + 入出力 API がわかりやすくなる定石。

オブジェクトを渡す: Parcelable

カスタムクラスを渡すには Parcelable 実装が必要。Kotlin なら @Parcelize で自動生成:

// build.gradle (Module)
// plugins { id 'kotlin-parcelize' }

import android.os.Parcelable
import kotlinx.parcelize.Parcelize

@Parcelize
data class User(
    val id: Int,
    val name: String,
    val email: String,
) : Parcelable

// 送信
val user = User(1, "Yamada", "y@example.com")
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("USER", user)
startActivity(intent)

// 受信
val user: User? = intent.getParcelableExtra("USER")
// API 33+ では型を明示
val user: User? = intent.getParcelableExtra("USER", User::class.java)

Serializable は使うな(基本)

Serializable はリフレクションを使うため Parcelable より遅く、推奨されません。古いコードでよく見る:

// ❌ 非推奨(パフォーマンス劣る)
data class User(val id: Int, val name: String) : Serializable

// ✅ 推奨
@Parcelize
data class User(val id: Int, val name: String) : Parcelable

Bundle で複数値を一括

val bundle = Bundle().apply {
    putInt("USER_ID", 42)
    putString("USER_NAME", "Yamada")
    putParcelable("USER", user)
}
val intent = Intent(this, DetailActivity::class.java)
intent.putExtras(bundle)
startActivity(intent)

// 受信
val bundle = intent.extras ?: Bundle()
val userId = bundle.getInt("USER_ID")

結果を受け取る: 旧 startActivityForResult は非推奨

Android 11 (API 30) で startActivityForResult非推奨になり、Jetpack の Activity Result API が標準です:

// ❌ 旧: 非推奨
override fun onActivityResult(reqCode: Int, resCode: Int, data: Intent?) {
    if (reqCode == 100 && resCode == RESULT_OK) {
        val value = data?.getStringExtra("RESULT")
    }
}

// ✅ 新: registerForActivityResult
class MainActivity : AppCompatActivity() {
    private val pickEditor = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
        if (result.resultCode == RESULT_OK) {
            val value = result.data?.getStringExtra("RESULT")
        }
    }

    private fun openEditor() {
        val intent = Intent(this, EditorActivity::class.java)
        pickEditor.launch(intent)
    }
}

// EditorActivity 側
fun finishWith(value: String) {
    val data = Intent().apply { putExtra("RESULT", value) }
    setResult(RESULT_OK, data)
    finish()
}

定義済み Contract

// 画像選択
val pickImage = registerForActivityResult(
    ActivityResultContracts.GetContent()
) { uri: Uri? -> /* ... */ }
pickImage.launch("image/*")

// 写真撮影
val takePicture = registerForActivityResult(
    ActivityResultContracts.TakePicture()
) { success: Boolean -> /* ... */ }

// 権限要求
val requestPermission = registerForActivityResult(
    ActivityResultContracts.RequestPermission()
) { granted: Boolean -> /* ... */ }
requestPermission.launch(Manifest.permission.CAMERA)

Navigation Component + Safe Args(モダン)

Single Activity + Fragment 構成の現代 Android 標準。型安全に引数を渡せます:

<!-- res/navigation/nav_graph.xml -->
<navigation>
    <fragment android:id="@+id/detailFragment"
        android:name="com.example.DetailFragment">
        <argument
            android:name="userId"
            app:argType="integer"
            android:defaultValue="-1" />
        <argument
            android:name="user"
            app:argType="com.example.User"
            app:nullable="false" />
    </fragment>
</navigation>
// 送信側: Safe Args で生成された Direction クラスを使用
val action = ListFragmentDirections.actionListToDetail(userId = 42, user = user)
findNavController().navigate(action)

// 受信側: navArgs() で型安全に取得
class DetailFragment : Fragment() {
    private val args: DetailFragmentArgs by navArgs()

    override fun onViewCreated(view: View, saved: Bundle?) {
        val userId = args.userId
        val user   = args.user
    }
}

ViewModel + SavedStateHandle

大きなオブジェクトや UI 状態は Intent ではなく ViewModel で保持し、SavedStateHandle で OS kill にも備える:

class DetailViewModel(private val state: SavedStateHandle) : ViewModel() {
    val userId: Int = state["userId"] ?: -1
    val userName: String = state["userName"] ?: ""

    fun updateName(name: String) {
        state["userName"] = name
    }
}

// 起動側で nav args / Intent extras 経由で渡す
class DetailActivity : AppCompatActivity() {
    val vm: DetailViewModel by viewModels()
}

Jetpack Compose での Argument

NavHost(navController, startDestination = "list") {
    composable("list") { ListScreen(navController) }
    composable(
        "detail/{userId}",
        arguments = listOf(navArgument("userId") { type = NavType.IntType })
    ) { backStack ->
        val userId = backStack.arguments?.getInt("userId") ?: -1
        DetailScreen(userId)
    }
}

// 遷移
navController.navigate("detail/42")

永続的に値を保持したい場合

用途推奨
軽い設定値SharedPreferences / DataStore (推奨)
構造化データRoom (SQLite ラッパ)
セキュアな情報EncryptedSharedPreferences
セッション中のみApplication class の field / Singleton

注意点

  • Intent に大きなオブジェクト(巨大画像/数 MB の Bitmap)を入れると TransactionTooLargeException
  • 渡せる総量は約 1 MB。大きいデータは ViewModel / DB / ファイル経由
  • Process Death(OS によるプロセス kill)対策に SavedStateHandle / onSaveInstanceState を活用
  • API 33+ の getParcelableExtra は型指定オーバーロードを推奨

FAQ

Q: 多数の引数を渡したい
A: data class を作って Parcelable 化 → 1 つの extra で渡すのがクリーンです。

Q: 巨大画像を渡したい
A: Intent ではなく URI(content://)を渡し、受信側で BitmapFactory.decodeStream。または ViewModel / Repository 経由。

Q: Fragment 間ならどう渡す?
A: Safe Args、または setFragmentResult / setFragmentResultListener(Fragment Result API)。

編集
Post Share
子ページ

子ページはありません

同階層のページ

同階層のページはありません

最近更新/作成されたページ