Жизненный цикл приложения
Понимание жизненного цикла приложения
Заголовок раздела «Понимание жизненного цикла приложения»Настольные приложения имеют жизненный цикл от запуска до завершения. Wails v3 предоставляет сервисы, события и хуки для эффективного управления этим жизненным циклом.
Этапы жизненного цикла
Заголовок раздела «Этапы жизненного цикла»1. Создание приложения
Заголовок раздела «1. Создание приложения»Создайте свое приложение с помощью application.New():
app := application.New(application.Options{ Name: "My App", Description: "An application built with Wails", Services: []application.Service{ application.NewService(&MyService{}), }, Assets: application.AssetOptions{ Handler: application.BundledAssetFileServer(assets), },})Что происходит:
- Опции анализируются и проверяются на валидность
- Сервисы регистрируются (но еще не запускаются)
- Настраивается сервер активов
- Настраивается среда выполнения (Runtime)
2. Запуск приложения
Заголовок раздела «2. Запуск приложения»Вызовите app.Run() для запуска приложения:
err := app.Run() // Blocks until quitif err != nil { log.Fatal(err)}Что происходит:
- Сервисы запускаются в порядке регистрации
- Активируются слушатели событий
- Можно создавать окна
- Начинается цикл обработки событий
3. Цикл обработки событий
Заголовок раздела «3. Цикл обработки событий»Приложение входит в цикл обработки событий, где оно проводит большую часть времени:
- Обрабатываются события ОС (мышь, клавиатура, события окон)
- Обрабатываются сообщения от Go к JS
- Выполняются вызовы от JS к Go
- Отображаются обновления пользовательского интерфейса
4. Завершение работы
Заголовок раздела «4. Завершение работы»Когда приложение завершает работу:
- Проверяется колбэк
ShouldQuit(если установлен) - Выполняются колбэки
OnShutdown - Сервисы останавливаются в порядке, обратном порядку регистрации
- Закрываются окна
- Освобождаются ресурсы
Жизненный цикл сервисов
Заголовок раздела «Жизненный цикл сервисов»Сервисы являются основным способом управления жизненным циклом в Wails v3. Они предоставляют хуки запуска и завершения через интерфейсы. Полную документацию по сервисам см. в руководстве по сервисам.
Создание сервиса
Заголовок раздела «Создание сервиса»type MyService struct { db *sql.DB}
// ServiceStartup is called when the application startsfunc (s *MyService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error { var err error s.db, err = sql.Open("sqlite3", "app.db") if err != nil { return err // Startup aborts if error returned }
// Run migrations if err := s.runMigrations(); err != nil { return err }
return nil}
// ServiceShutdown is called when the application shuts downfunc (s *MyService) ServiceShutdown() error { if s.db != nil { return s.db.Close() } return nil}Регистрация сервисов
Заголовок раздела «Регистрация сервисов»app := application.New(application.Options{ Services: []application.Service{ application.NewService(&MyService{}), application.NewService(&AnotherService{}), },})Ключевые моменты:
- Сервисы запускаются в порядке регистрации
- Сервисы останавливаются в порядке, обратном порядку регистрации
- Если
ServiceStartupсервиса возвращает ошибку, приложение прерывает запуск - Контекст
ctx, переданный вServiceStartup, отменяется при начале завершения работы
Использование контекста приложения
Заголовок раздела «Использование контекста приложения»Контекст, переданный в ServiceStartup, действителен на протяжении всего времени жизни приложения:
func (s *MyService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error { // Start a background task that respects shutdown go func() { ticker := time.NewTicker(5 * time.Minute) defer ticker.Stop()
for { select { case <-ticker.C: s.performBackgroundSync() case <-ctx.Done(): // Application is shutting down return } } }()
return nil}Вы также можете получить доступ к контексту из экземпляра приложения:
app := application.Get()ctx := app.Context()Хуки уровня приложения
Заголовок раздела «Хуки уровня приложения»Это удобные колбэки в application.Options, которые позволяют подключаться к жизненному циклу приложения без создания полноценного сервиса. Они полезны для простых задач очистки, подтверждения выхода или когда вам нужно выполнить код в определенных точках последовательности завершения работы.
Для более сложного управления жизненным циклом с логикой запуска, внедрением зависимостей или управляемыми ресурсами используйте Сервисы.
ShouldQuit
Заголовок раздела «ShouldQuit»Колбэк ShouldQuit вызывается всякий раз, когда запрашивается выход — будь то пользователь, закрывший последнее окно, нажавший Cmd+Q (macOS) / Alt+F4 (Windows), или программный вызов app.Quit().
Возвращаемое значение:
- Верните
true, чтобы разрешить выход (приложение завершит работу) - Верните
false, чтобы отменить выход (приложение продолжит работу)
Это ваш шанс перехватить запросы на выход и, при необходимости, предотвратить их, например, чтобы запросить у пользователя сохранение несохраненных изменений:
app := application.New(application.Options{ ShouldQuit: func() bool { if !hasUnsavedChanges() { return true // No unsaved changes, allow quit }
// Prompt the user result, _ := application.QuestionDialog(). SetTitle("Unsaved Changes"). SetMessage("You have unsaved changes. Quit anyway?"). AddButton("Quit", "quit"). AddButton("Cancel", "cancel"). Show()
// Only quit if user clicked "Quit" return result == "quit" },})Если ShouldQuit не установлен, приложение завершит работу немедленно при запросе.
Когда вызывается ShouldQuit:
- Пользователь закрывает последнее окно (если не установлен
DisableQuitOnLastWindowClosed) - Пользователь нажимает Cmd+Q на macOS
- Пользователь нажимает Alt+F4 на Windows (когда фокус находится на последнем окне)
- Код вызывает
app.Quit()
Когда ShouldQuit НЕ вызывается:
- Процесс убит (SIGKILL, принудительное завершение через Диспетчер задач)
- Вызван
os.Exit()напрямую
OnShutdown
Заголовок раздела «OnShutdown»Колбэк OnShutdown вызывается, когда выход из приложения подтвержден (после того, как ShouldQuit вернул true, если он был установлен). Используйте его для задач очистки, таких как сохранение состояния, закрытие соединений с базой данных или освобождение ресурсов.
app := application.New(application.Options{ OnShutdown: func() { // Save application state saveState()
// Close connections----------|--------------------------------|| macOS | Приложение продолжает работать (меню остается видимым) || Windows | Приложение завершает работу || Linux | Приложение завершает работу |
macOS следует родным платформенным соглашениям, согласно которым приложения обычно остаются активными в строке меню даже при отсутствии открытых окон. Windows и Linux по умолчанию завершают работу.
**Заставить все платформы завершать работу при закрытии последнего окна:**
```goapp := application.New(application.Options{ Mac: application.MacOptions{ ApplicationShouldTerminateAfterLastWindowClosed: true, },})Заставить все платформы продолжать работу при закрытии последнего окна:
Это полезно для приложений в системном трее или приложений, которые должны оставаться активными в фоновом режиме.
app := application.New(application.Options{ Windows: application.WindowsOptions{ DisableQuitOnLastWindowClosed: true, }, Linux: application.LinuxOptions{ DisableQuitOnLastWindowClosed: true, },})Распространённые паттерны
Заголовок раздела «Распространённые паттерны»Паттерн 1: Сервис базы данных
Заголовок раздела «Паттерн 1: Сервис базы данных»type DatabaseService struct { db *sql.DB}
func (s *DatabaseService) ServiceStartup(ctx context.Context, options application.ServiceOptions) error { var err error s.db, err = sql.Open("sqlite3", "app.db") if err != nil { return fmt.Errorf("failed to open database: %w", err) }
if err := s.db.PingContext(ctx); err != nil { return fmt.Errorf("failed to connect to database: %w", err) }
return nil}**Вопросы о жизненном цикле?** Задавайте их в [Discord](https://discord.gg/JDdSxwjhGf) или смотрите [примеры](https://github.com/wailsapp/wails/tree/master/v3/examples).