Как работает цикл жизни страницы (Page Lifecycle)?

В Xamarin.Forms жизненный цикл страницы (Page Lifecycle) — это последовательность событий и методов, которые вызываются при создании, отображении, скрытии и уничтожении страницы (Page). Понимание этого цикла важно для правильной инициализации данных, освобождения ресурсов, обновления интерфейса и обработки навигации.

Страницы в Xamarin.Forms, такие как ContentPage, NavigationPage, TabbedPage, MasterDetailPage (или FlyoutPage) — являются основными строительными блоками пользовательского интерфейса. Каждая из них проходит через определённые стадии, начиная от создания и заканчивая уничтожением или повторной активацией.

Основные этапы жизненного цикла страницы

  1. **Создание экземпляра страницы (Constructor)
    **
  2. **OnAppearing() — страница отображается
    **
  3. **OnDisappearing() — страница скрывается
    **
  4. **Уничтожение (GC, если страница не используется)
    **

Подробный разбор жизненного цикла

1. Конструктор (Constructor)

public MainPage()
{
InitializeComponent();
// Здесь создаются компоненты и инициализируются переменные
}
  • Вызывается при создании экземпляра страницы.

  • Выполняется один раз за "жизнь" страницы, если не используется повторная инициализация или кэширование.

  • Здесь можно задавать значения по умолчанию, подписываться на события, вызывать сервисы.

Важно: UI ещё не полностью сформирован, привязки могут быть неактивны.

2. OnAppearing()

protected override void OnAppearing()
{
base.OnAppearing();
// Код для загрузки данных, обновления интерфейса и подписки на события
}
  • Вызывается каждый раз, когда страница становится видимой для пользователя.

  • Отличается от конструктора тем, что может вызываться многократно, например, при возврате с другой страницы.

  • Используется для:

    • Загрузки/обновления данных

    • Обновления UI

    • Подписки на события

    • Запуска анимаций

    • Начала отслеживания GPS/датчиков

Пример: загрузить список сообщений при входе на страницу "Входящие".

3. OnDisappearing()

protected override void OnDisappearing()
{
base.OnDisappearing();
// Код для очистки ресурсов и отписки от событий
}
  • Вызывается каждый раз, когда страница перестаёт быть видимой.

  • Например:

    • При переходе на другую страницу

    • При сворачивании приложения (на некоторых платформах)

  • Используется для:

    • Сохранения состояния

    • Остановки таймеров

    • Отписки от событий

    • Освобождения ресурсов (например, подключения к БД)

Важно: OnDisappearing() не вызывается при закрытии приложения (если ОС не инициирует завершение корректно).

4. Уничтожение страницы (Garbage Collection)

  • Когда страница более не используется и на неё нет ссылок, сборщик мусора (GC) может освободить память.

  • Разработчику важно отписывать события и уничтожать ресурсы, чтобы предотвратить утечки памяти.

  • Специального метода OnDestroyed() не существует, но можно использовать IDisposable.

Пример:

public class MyPage : ContentPage, IDisposable
{
private bool disposed = false;
public void Dispose()
{
if (disposed) return;
// Очистка ресурсов
disposed = true;
}
}

Повторное использование страниц

Если используется Navigation.PushAsync(page), то каждый раз создаётся новая страница.
Если вы предварительно создали страницу один раз и повторно вызываете Navigation.PushAsync(savedPage), жизненный цикл будет отличаться:

  • Constructor вызывается один раз.

  • OnAppearing / OnDisappearing — каждый раз при отображении/скрытии.

Это позволяет сохранять состояние страницы между посещениями (например, вкладки в TabbedPage).

Жизненный цикл с навигацией

Действие Страница A Страница B
Создание A Constructor, OnAppearing
--- --- ---
Переход на B (PushAsync) OnDisappearing Constructor, OnAppearing
--- --- ---
Возврат на A (PopAsync) OnAppearing OnDisappearing
--- --- ---

Асинхронная инициализация

Так как OnAppearing() — это void, нельзя напрямую использовать await. Чтобы загружать данные асинхронно, нужно вызывать асинхронные методы вручную:

protected override void OnAppearing()
{
base.OnAppearing();
_ = LoadDataAsync(); // "fire-and-forget"
}
private async Task LoadDataAsync()
{
IsBusy = true;
var data = await DataService.GetDataAsync();
Items = new ObservableCollection<Item>(data);
IsBusy = false;
}

Использование в MVVM

В MVVM логика жизненного цикла может быть реализована во ViewModel. Есть несколько способов:

1. Через событийную привязку в OnAppearing

protected override void OnAppearing()
{
base.OnAppearing();
(BindingContext as MyViewModel)?.OnPageAppearing();
}

2. Через интерфейс (например, IPageLifecycleAware)

Можно создать интерфейс:

public interface IPageLifecycleAware
{
void OnAppearing();
void OnDisappearing();
}

И реализовать его в ViewModel, вызывать вручную из Page.

Отличия на Android и iOS

  • В iOS и Android внутренние жизненные циклы (OnPause, OnResume, ViewWillAppear, и т.д.) обрабатываются Xamarin.Forms и транслируются в OnAppearing / OnDisappearing.

  • Поведение может различаться в зависимости от версии Xamarin.Forms, например:

    • В TabbedPage страницы могут не вызывать OnAppearing, если уже инициализированы.

    • В NavigationPage OnAppearing может вызываться при возврате, даже если страница не была удалена.

Расширенные события и трюки

Использование MessagingCenter

Если вы хотите получать уведомления о событиях вне самой страницы:

MessagingCenter.Send(this, "PageAppeared");
MessagingCenter.Subscribe<Page>(this, "PageAppeared", sender => {
// Обработка
});

Использование EventToCommand

В XAML можно подписать событие на команду ViewModel с помощью библиотек, таких как Prism или CommunityToolkit.

Сводка жизненного цикла

Стадия Метод Вызывается
Инициализация Constructor Один раз, при создании страницы
--- --- ---
Отображение OnAppearing Каждый раз при переходе на страницу
--- --- ---
Скрытие OnDisappearing При уходе со страницы
--- --- ---
Удаление GC / Dispose Если страница не используется
--- --- ---

Жизненный цикл страницы в Xamarin.Forms — это ключевой механизм управления ресурсами, состоянием и отображением данных. Правильное понимание порядка событий позволяет разработчику писать оптимальный и безопасный код, особенно в архитектуре MVVM и при работе с асинхронными источниками данных.