Какие плюсы и минусы подхода Single Activity?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Single Activity архитектура: плюсы и минусы
Single Activity (одна Activity на приложение) — это архитектурный паттерн, когда всё приложение использует одну Activity и управляет навигацией через Fragments. Это стандарт в современной Android разработке, особенно с Navigation Component.
Архитектура Single Activity
// MainActivity — единственная Activity
class MainActivity : AppCompatActivity() {
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
}
}
// nav_graph.xml определяет навигацию между Fragments
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/nav_graph">
<fragment android:id="@+id/homeFragment" android:name=".HomeFragment" />
<fragment android:id="@+id/detailFragment" android:name=".DetailFragment" />
<action android:id="@+id/action_home_to_detail"
app:destination="@id/detailFragment" />
</navigation>
ПЛЮСЫ Single Activity
1. Управление состоянием проще
// Состояние приложения централизовано в ViewModel'е
class AppViewModel : ViewModel() {
private val _appState = MutableLiveData<AppState>()
val appState: LiveData<AppState> = _appState
// Все экраны используют один ViewModel
// Состояние сохраняется при navigation
}
// Во фрагментах
class HomeFragment : Fragment() {
private val appViewModel: AppViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
appViewModel.appState.observe(viewLifecycleOwner) { state ->
// Всегда видим актуальное состояние
updateUI(state)
}
}
}
2. Более простой back stack
// Back stack управляется NavController, не Activity Manager
navController.navigate(R.id.action_home_to_detail) // Добавляет в back stack
navController.popBackStack() // Удаляет из back stack
// Жизненный цикл более предсказуем
// onPause/onResume вызываются один раз
3. Переходы между экранами без запуска новой Activity
// Нет overhead на создание новой Activity
// Быстрее и меньше памяти
// ❌ Multi-Activity подход
startActivity(Intent(this, DetailsActivity::class.java).apply {
putExtra("user_id", userId) // Передача данных через Intent
})
// ✅ Single Activity подход
val action = HomeFragmentDirections.actionHomeToDetail(userId)
navController.navigate(action) // Type-safe navigation
4. Улучшенная навигация с Deep Links
<fragment android:id="@+id/detailFragment"
android:name=".DetailFragment">
<deep-link app:uri="app://detail/{userId}" />
<argument android:name="userId" app:type="integer" />
</fragment>
// Deep link автоматически создаёт правильный back stack
val deepLink = "app://detail/123"
navController.navigate(deepLink.toUri())
5. Сохранение состояния при rotation
class HomeFragment : Fragment() {
private val viewModel: HomeViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// ViewModel сохраняет состояние при rotation
viewModel.items.observe(viewLifecycleOwner) { items ->
adapter.submitList(items)
}
}
}
// При rotation Fragment пересоздаётся, но ViewModel остаётся
6. Единая точка входа для всех переходов
class AppNavigator(private val navController: NavController) {
fun navigateToHome() = navController.navigate(R.id.homeFragment)
fun navigateToDetail(id: Int) = navController.navigate(
HomeFragmentDirections.actionHomeToDetail(id)
)
fun navigateToSettings() = navController.navigate(R.id.settingsFragment)
}
МИНУСЫ Single Activity
1. Более сложное управление transition анимациями
// ❌ Multi-Activity проще с ActivityOptions
startActivity(
Intent(this, DetailActivity::class.java),
ActivityOptions.makeSceneTransitionAnimation(this).toBundle()
)
// ✅ Single Activity требует более явного управления
navigator.navigateToDetail(id) { transitionDuration = 300 }
// Нужно настраивать в fragment transaction
val options = FragmentNavigatorExtras(
sharedView to "shared_element_name"
)
navController.navigate(action, options)
2. Все Fragments конкурируют за ресурсы
// ❌ Проблема: оба Fragment'а видны
class HomeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Может быть скрыт, но всё ещё обновляет UI
startObservingData() // Работает в фоне
}
}
// ✅ Решение: pause/resume наблюдения
class HomeFragment : Fragment() {
override fun onPause() {
super.onPause()
viewModel.pause() // Остановить обновления
}
override fun onResume() {
super.onResume()
viewModel.resume() // Возобновить обновления
}
}
3. Более сложное управление Dialogs и PopUps
// ❌ Multi-Activity: можно легко открыть Dialog Activity
startActivityForResult(Intent(this, DialogActivity::class.java), REQUEST_CODE)
// ✅ Single Activity: нужно использовать DialogFragment
class MyDialogFragment : DialogFragment() {
interface OnResultListener {
fun onResult(result: String)
}
private var listener: OnResultListener? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<Button>(R.id.button_ok).setOnClickListener {
listener?.onResult("Result")
dismiss()
}
}
}
4. Сложность с background задачами
// ❌ Multi-Activity: можно запустить Service
startService(Intent(this, MyService::class.java))
// ✅ Single Activity: нужно использовать WorkManager или Coroutines
class HomeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.data.collect { data ->
updateUI(data)
}
}
}
}
}
5. Отладка back stack может быть сложнее
// ❌ Сложно понять, какие Fragment'ы в back stack'е
navController.currentBackStackEntry?.destination?.label
// ✅ Нужно логировать явно
navController.addOnDestinationChangedListener { _, destination, _ ->
Log.d("Navigation", "Navigated to: ${destination.label}")
}
6. Может быть сложнее с модульной архитектурой
// ❌ Сложно: у каждого модуля свой Activity
module_auth::AuthActivity
module_profile::ProfileActivity
module_shop::ShopActivity
// ✅ Решение: shared navigation module
module_navigation::NavigationGraph
// Все модули используют одну Activity и один graph
Когда использовать Single Activity
- Большинство современных приложений
- Если нужна гибкая навигация между экранами
- Если используется Material Design
- Если нужно делиться состоянием между экранами
Когда использовать Multi-Activity
- Отдельные экраны с кардинально отличающимся UI
- Отдельные приложения в составе multi-app
- Legacy приложения
Вывод: Single Activity + Navigation Component — стандарт современной Android разработки, рекомендуемый Google.