Как создать ViewModel? Почему создавать ViewModel нужно именно так?android-62

Создание ViewModel в Android

1. Базовый способ через ViewModelProvider

Стандартный подход с использованием ViewModelProvider, который привязывает ViewModel к жизненному циклу владельца (Activity/Fragment).

class MyViewModel : ViewModel() {
    val liveData = MutableLiveData<String>()
}

// В Activity/Fragment:
val viewModel = ViewModelProvider(this).get(MyViewModel::class.java)

Почему так?

  • ViewModelProvider гарантирует, что ViewModel переживет изменения конфигурации (например, поворот экрана).
  • Если ViewModel уже существует (после поворота), возвращается существующий экземпляр, а не создается новый.

2. Использование делегата by viewModels

Упрощенный синтаксис с Kotlin Property Delegates.

// Для Activity:
val viewModel: MyViewModel by viewModels()

// Для Fragment (с учетом родительского ViewModel):
val viewModel: SharedViewModel by activityViewModels()

Почему так?

  • Избавляет от бойлерплейт-кода (ViewModelProvider).
  • Лучшая читаемость и поддержка.

3. ViewModel с параметрами

Если ViewModel требует аргументов (например, репозиторий).

class MyViewModel(private val repo: MyRepository) : ViewModel() { ... }

class MyViewModelFactory(private val repo: MyRepository) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return MyViewModel(repo) as T
    }
}

// Использование:
val factory = MyViewModelFactory(repository)
val viewModel = ViewModelProvider(this, factory).get(MyViewModel::class.java)

Почему так?

  • ViewModel не должна знать о способе создания своих зависимостей (принцип Dependency Injection).
  • Factory позволяет передавать параметры, сохраняя жизненный цикл ViewModel.

4. Использование Hilt для внедрения ViewModel

Современный подход с библиотекой Hilt (упрощенный DI от Google).

@HiltViewModel
class MyViewModel @Inject constructor(
    private val repo: MyRepository
) : ViewModel() { ... }

// В Activity/Fragment (автоматическое создание):
@AndroidEntryPoint
class MyActivity : AppCompatActivity() {
    private val viewModel: MyViewModel by viewModels()
}

Почему так?

  • Полностью автоматизирует создание ViewModel с зависимостями.
  • Избавляет от ручного написания Factory.

Почему ViewModel создается именно через ViewModelProvider/Factory?

  1. Сохранение состояния при повороте экрана
    ViewModel не уничтожается при изменении конфигурации, в отличие от Activity/Fragment.

  2. Избегание утечек памяти
    ViewModel автоматически очищается, когда связанный жизненный цикл (Activity/Fragment) полностью уничтожен (не из-за поворота).

  3. Единый источник истины
    Все UI-компоненты (например, несколько Fragments в одной Activity) могут использовать одну ViewModel через activityViewModels().

  4. Тестируемость
    Паттерн Factory и DI позволяют легко подменять зависимости в тестах.


Резюмируем

  • Стандартный способ: ViewModelProvider(this).get(MyViewModel::class.java).
  • Для Kotlin: Упрощенный синтаксис с by viewModels().
  • С параметрами: Через кастомную ViewModelProvider.Factory.
  • С Hilt: Аннотация @HiltViewModel + by viewModels().
  • Важно: ViewModel всегда создается через систему провайдеров, чтобы сохранить состояние и избежать утечек.