← Назад к вопросам

Почему не стоит писать код в одной Activity?

1.0 Junior🔥 211 комментариев
#Android компоненты#Жизненный цикл и навигация

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Почему не стоит писать весь код в одной Activity

Основная проблема размещения всей логики приложения в одной Activity заключается в нарушении фундаментального принципа SOLID, а именно — Single Responsibility Principle (Принцип единой ответственности). Activity становится God Object (Божественным объектом), который знает и делает слишком много, что приводит к множеству негативных последствий для поддержки, тестирования и масштабирования проекта.

Ключевые проблемы "раздутой" Activity:

  1. Сложность поддержки и чтения кода
    Класс превращается в монстра на тысячи строк. Найти нужный метод, понять логику работы или внести изменение становится невероятно сложной задачей даже для автора кода спустя время.

```kotlin
// ПЛОХО: Всё в одной Activity
class MainActivity : AppCompatActivity() {
    // 1. Работа с View
    private lateinit var recyclerView: RecyclerView
    private lateinit var adapter: MyAdapter
    private lateinit var button: Button

    // 2. Работа с сетью
    private val retrofit = Retrofit.Builder()...build()
    private val apiService: ApiService = retrofit.create()

    // 3. Работа с базой данных
    private lateinit var db: AppDatabase

    // 4. Логика навигации
    // 5. Обработка жизненного цикла
    // 6. Обработка разрешений
    // 7. Бизнес-логика
    // ... тысячи строк кода
}
```

2. Слабый контроль над жизненным циклом и утечки памяти

    Activity, переживающая конфигурационные изменения (поворот экрана), должна корректно сохранять и восстанавливать состояние. Когда в ней находятся сетевые запросы, базы данных или другие долгоживущие объекты, резко возрастает риск **утечек памяти (Memory Leaks)**, так как легко удерживать ссылки на контекст Activity из фоновых потоков.

```kotlin
// РИСК УТЕЧКИ: Запрос удерживает ссылку на Activity
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Запрос продолжит жить после уничтожения Activity
    apiService.getData().enqueue(object : Callback<Data> {
        override fun onResponse(call: Call<Data>, response: Response<Data>) {
            // Упс! Если Activity уже уничтожена, это вызовет краш или утечку.
            updateUI(response.body())
        }
    })
}
```

3. Затруднённое модульное тестирование

    Код, жёстко привязанный к **Android SDK** (контекст, View, жизненный цикл), крайне сложно тестировать в изоляции (юнит-тестах) на JVM без эмулятора. Бизнес-логика, перемешанная с кодом UI, становится непроверяемой.

  1. Невозможность переиспользования кода
    Логика, зашитая в конкретную Activity, не может быть использована в другом месте приложения (например, во **Fragment** или другой Activity) или в другом проекте. Это дублирование усилий.

  1. Хрупкость и сильная связанность (Tight Coupling)
    Изменение одного элемента интерфейса или источника данных может потребовать правок в десятках мест по всему гигантскому классу. Система становится хрупкой, а стоимость изменений — высокой.

Архитектурные решения: Разделение ответственности

Современные подходы рекомендуют разделять код на слои, используя паттерны типа MVP, MVVM или MVI в сочетании с Clean Architecture.

// ХОРОШО: Разделение на слои (упрощённый MVVM с Clean Architecture)

// 1. Domain Layer (не зависит от Android) - Бизнес-логика
class GetUserUseCase(private val repository: UserRepository) {
    suspend operator fun invoke(userId: String): User = repository.getUser(userId)
}

// 2. Data Layer - Работа с данными (сеть, БД)
class UserRepositoryImpl(
    private val api: ApiService,
    private val db: UserDao
) : UserRepository {
    override suspend fun getUser(id: String): User {
        // Кэширование, выбор стратегии и т.д.
        return api.fetchUser(id)
    }
}

// 3. Presentation Layer - ViewModel (логика отображения, выживает при повороте)
class UserViewModel(
    private val getUserUseCase: GetUserUseCase
) : ViewModel() {
    private val _userState = MutableStateFlow<UserState>(UserState.Loading)
    val userState: StateFlow<UserState> = _userState

    fun loadUser(id: String) {
        viewModelScope.launch {
            _userState.value = UserState.Loading
            try {
                val user = getUserUseCase(id)
                _userState.value = UserState.Success(user)
            } catch (e: Exception) {
                _userState.value = UserState.Error(e.message)
            }
        }
    }
}

// 4. UI Layer - Activity/Fragment (только отображение и обработка ввода)
class UserActivity : AppCompatActivity() {
    private val viewModel: UserViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user)

        // Наблюдение за состоянием из ViewModel
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.userState.collect { state ->
                    when (state) {
                        is UserState.Success -> showUser(state.user)
                        is UserState.Error -> showError(state.message)
                        UserState.Loading -> showProgressBar()
                    }
                }
            }
        }

        // Обработка ввода пользователя - триггер для логики во ViewModel
        buttonRefresh.setOnClickListener {
            viewModel.loadUser("123")
        }
    }
}

Преимущества такого подхода:

  • Тестируемость: GetUserUseCase и UserViewModel можно покрыть юнит-тестами без Android-зависимостей.
  • Управление жизненным циклом: ViewModel переживает поворот экрана, сетевые запросы безопасно запускаются в viewModelScope.
  • Читаемость и поддержка: Каждый класс имеет одну чёткую зону ответственности.
  • Переиспользование: GetUserUseCase может быть вызван из разных мест приложения.
  • Гибкость: Источник данных в UserRepository можно изменить (с API на БД), не трогая ViewModel или Activity.

Вывод: Отказ от помещения всего кода в одну Activity — это не просто "хороший тон", а необходимое условие для создания стабильного, поддерживаемого и тестируемого приложения. Это требует дисциплины и применения современных архитектурных паттернов, но многократно окупается на этапах разработки, отладки и масштабирования проекта.

Почему не стоит писать код в одной Activity? | PrepBro