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

Как работает делегат viewModels?

2.2 Middle🔥 241 комментариев
#Android компоненты#Архитектура и паттерны

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

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

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

Как работает делегат viewModels() в Android

Делегат viewModels() — это мощный инструмент из библиотеки AndroidX Lifecycle, который предоставляет лаконичный и безопасный способ создания и управления ViewModel в рамках компонентов Android, таких как Activity и Fragment. Его основная задача — обеспечить правильный жизненный цикл ViewModel, чтобы данные сохранялись при изменениях конфигурации (например, повороте экрана) и очищались, когда владелец (Activity/Fragment) завершает свою работу.

Механизм работы

1. Делегирование свойства Kotlin

Делегат viewModels() использует механизм делегирования свойств Kotlin (by), что позволяет инстанциировать ViewModel лениво (при первом обращении) и автоматически управлять её жизненным циком.

Пример использования во Fragment:

class MyFragment : Fragment() {
    private val viewModel: MyViewModel by viewModels()
    // или с фабрикой для параметризованного ViewModel:
    // private val viewModel: MyViewModel by viewModels { MyViewModelFactory(someParam) }
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewModel.data.observe(viewLifecycleOwner) { data ->
            // Обновление UI
        }
    }
}

2. Ленивая инициализация

ViewModel создаётся только при первом обращении к свойству viewModel. Это реализуется через Lazy<T> интерфейс Kotlin:

public inline fun <reified VM : ViewModel> Fragment.viewModels(
    noinline ownerProducer: () -> ViewModelStoreOwner = { this },
    noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
    return createViewModelLazy(VM::class, { ownerProducer().viewModelStore }, factoryProducer)
}

3. Привязка к жизненному циклу владельца (ViewModelStoreOwner)

Ключевые компоненты:

  • ViewModelStoreOwner — интерфейс, который предоставляет ViewModelStore (хранилище ViewModel). Activity и Fragment реализуют этот интерфейс.
  • ViewModelStore — контейнер, который хранит ViewModel по ключам (обычно использует Canonical Name класса ViewModel).
  • ViewModelProvider — фабрика, которая создает или возвращает существующий экземпляр ViewModel из ViewModelStore.

При изменении конфигурации Activity/Fragment пересоздаются, но ViewModelStore сохраняется системой через onRetainNonConfigurationInstance() (в Activity) или setRetainInstance(true) (во Fragment, но устаревший способ). Современный подход использует ViewModel, который автоматически сохраняется системой.

4. Поток создания ViewModel

Когда вы впервые обращаетесь к viewModel свойству:

  1. Проверяется, есть ли уже экземпляр в ViewModelStore текущего владельца (Fragment/Activity)
  2. Если есть — возвращается существующий экземпляр
  3. Если нет — создаётся новый через ViewModelProvider.Factory
  4. Сохраняется в ViewModelStore под ключом "androidx.lifecycle.ViewModelProvider.DefaultKey:com.example.MyViewModel"
  5. Привязывается к жизненному циклу владельца

5. Различные варианты использования

Во Fragment с кастомной фабрикой:

private val viewModel: MyViewModel by viewModels {
    object : ViewModelProvider.Factory {
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return MyViewModel(repository) as T
        }
    }
}

В Activity:

class MyActivity : AppCompatActivity() {
    private val viewModel: MyViewModel by viewModels()
}

Использование ViewModel из родительского Fragment:

private val parentViewModel: SharedViewModel by viewModels(
    ownerProducer = { requireParentFragment() }
)

Преимущества делегата viewModels()

  • Безопасность жизненного цикла: ViewModel автоматически очищается, когда владелец завершает работу (Activity — после onDestroy(), Fragment — после onDestroy() если не является целевым для Navigation).
  • Сохранение состояния: Данные переживают изменения конфигурации без ручного сохранения в Bundle.
  • Ленивая инициализация: Экономия ресурсов, пока ViewModel не нужна.
  • Чистота кода: Избавляет от шаблонного кода ручного создания ViewModel через ViewModelProvider.
  • Scoping поддержка: Возможность указывать scope через ownerProducer (например, использовать ViewModel из родительского Fragment или Activity).

Внутренняя реализация

Под капотом метод viewModels() использует ViewModelLazy класс, который реализует Lazy<VM>. При первом обращении к свойству вызывается:

override val value: VM
    get() {
        val viewModelStore = storeProducer()
        val key = "androidx.lifecycle.ViewModelProvider.DefaultKey:$VMJVM_NAME"
        return ViewModelProvider(viewModelStore, factoryProducer()).get(key, VM::class.java)
    }

Важно отметить, что делегат viewModels() во Fragment по умолчанию привязывается к viewLifecycleOwner, а не к жизненному циклу самого Fragment. Это предотвращает утечки памяти и обеспечивает правильную очистку LiveData observers при уничтожении View.

Таким образом, делегат viewModels() абстрагирует сложность управления жизненным циклом ViewModel, предоставляя разработчику простой и безопасный API для работы с архитектурным компонентом ViewModel в современных Android-приложениях.