Как передавать параметры в Android через Intent, Bundle и query parameters

Короткий ответ: для обмена между Activity/Fragment используйте Intent extras/Bundle (примитивы и Parcelable), для deep links и навигации — query parameters в URI; для больших объектов передавайте только id и храните данные во ViewModel/DB.

Intent extras и Bundle — быстрое руководство с примерами

Отправка простых типов — через putExtra, ключи лучше хранить в константах:

// Отправитель
val intent = Intent(this, DetailActivity::class.java).apply {
    putExtra(EXTRA_USER_ID, 123L)
    putExtra(EXTRA_NAME, "Иван Иванов")
}
startActivity(intent)

// Получатель
val userId = intent.getLongExtra(EXTRA_USER_ID, -1L)
val name = intent.getStringExtra(EXTRA_NAME) ?: "Неизвестно"

Для Fragment — устанавливайте arguments до attach():

val bundle = Bundle().apply { putParcelable("user", user) }
val frag = DetailFragment().apply { arguments = bundle }

Ключи для extras храните как const val в companion object, чтобы избежать опечаток.

Передача объектов: Parcelable vs Serializable vs JSON

Используйте Parcelable (или @Parcelize) для собственных моделей — быстрее и компактнее. Serializable — запасной вариант для сторонних классов. Для кросс-платформенных/удалённых передач — JSON строка.

Сравнение способов передачи объектов

СпособПроизводительностьРазмерКогда применять
ParcelableОчень быстроМеньшеСобственные модели
SerializableМедленнееБольшеLegacy/библиотеки
JSON (String)СреднеБольшеДля логирования/интернета

Пример @Parcelize:

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

intent.putExtra("user", user)
val received: User? = intent.getParcelableExtra("user")

Для deep link используйте Uri с query-параметрами:

val uri = Uri.parse("app://detail").buildUpon()
    .appendQueryParameter("id", "456")
    .appendQueryParameter("filter", "new")
    .build()
val intent = Intent(Intent.ACTION_VIEW, uri)
startActivity(intent)

В Activity/Fragment получить:

val id = intent.data?.getQueryParameter("id")

В Jetpack Navigation (Compose) аргументы парсятся через NavType:

composable(
  "detail/{id}?filter={filter}",
  arguments = listOf(
    navArgument("id") { type = NavType.LongType },
    navArgument("filter") { defaultValue = "all"; nullable = true }
  )
) { backStackEntry ->
  val id = backStackEntry.arguments?.getLong("id")
  if (id == null) { /* обработать, finish() или показать ошибку */ }
}

Для динамических экранов (поиск, фильтры) query-параметры удобнее; для важных данных — передавайте id и загружайте модель через ViewModel/репозиторий.

Безопасные геттеры и совместимость с Android 13+ (TIRAMISU)

Используйте универсальный метод для получения Parcelable, чтобы избегать ClassCastException на новых API:

inline fun <reified T : Parcelable> Intent.getParcelableSafe(key: String): T? =
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        getParcelableExtra(key, T::class.java)
    } else {
        @Suppress("DEPRECATION")
        getParcelableExtra(key) as? T
    }

Частые ошибки

  • Не проверять null: всегда использовать дефолтные значения или requireNotNull.
  • Хранить большие объекты в extras — риск OOM/медленной сериализации.
  • Менять ключи extras без бэкапа — краши при чтении старых Intent.
  • Передавать mutable ссылки между Activity/Fragment — утечки памяти.

FAQ

  • Как передать список объектов? — Лучше сериализовать в ParcelableArrayList или передать id-list и восстановить модель из репозитория.
  • Что безопаснее: query params или extras? — Для навигации/ссылок — query params; для приватных данных между компонентами — extras.
  • Как избежать NullPointerException при парсинге id? — Используйте getLongExtra с дефолтом или проверку на null и finish()/showError.
  • Когда применять Safe Args? — При использовании Navigation Component: генерирует типобезопасный код и уменьшает ошибки с ключами.

Не передавайте пользовательские пароли или токены через Intent/URI — используйте безопасное хранилище (Keystore) и передавайте только маркеры/идентификаторы.