Active — активното състояние на жизнения цикъл на iOS приложение, в което то се намира на преден план, получава събития на допир и взаимодейства с потребителя. Разбираме как работи състоянието Active, кои методи на делегата UIApplicationDelegate отговарят за него и как правилно да обработваме преходите между Active и Inactive в Swift.
Основни
Active — състояние на жизнения цикъл на мобилно приложение, в което то се намира на преден план, показва се на екрана на устройството и активно взаимодейства с потребителя. В това състояние приложението получава всички събития на допир, натискания на клавиши, данни от акселерометъра и жироскопа, както и има пълен достъп до графичния процесор за рендиране на интерфейса.
На iOS състоянието Active е част от петсъстоятелния модел на жизнения цикъл: Not Running → Inactive → Active → Inactive → Background → Suspended → Not Running. На Android аналогът е състоянието на Activity след извикване на onResume, когато Activity се намира на върха на стека и приема потребителски вход. Active е единственото състояние, в което UI е напълно интерактивен и реагира на жестове, скрол, кликвания и анимации.
Системата предоставя на приложението в Active максимален приоритет по процесор и RAM. Това означава, че системата няма да прекрати такова приложение при недостиг на ресурси — първо ще бъдат разтоварени фоновете и спрените процеси. Приложението обаче трябва да използва ресурсите ефективно, за да не разрежда батерията и да не предизвиква throttling на CPU.
За потребителя Active е нормалното състояние на работа с приложението. Потребителят вижда интерфейса, може да натиска бутони, да попълва формуляри, да скролира фийда. Всяко прекъсване на това състояние (повикване, известие, плъзгане нагоре за Control Center) прехвърля приложението в Inactive, след което то може да се върне в Active или да отиде в Background.
iOS използва UIApplicationMain за управление на състоянието. При преход в Active системата извиква applicationDidBecomeActive. За SwiftUI аналогичният механизъм е наблюдение на scenePhase чрез Environment. Android използва onResume като индикатор за активност на Activity на преден план. И двата подхода гарантират, че приложението получава известие за промяна на състоянието и може да адаптира поведението си.
| Платформа | Метод/събитие | Swift (UIKit) | SwiftUI | Android (Kotlin) |
|---|---|---|---|---|
| iOS | Преход в Active | applicationDidBecomeActive | scenePhase == .active | — |
| iOS | Излизане от Active | applicationWillResignActive | scenePhase == .inactive | — |
| Android | Преход в Active | — | — | onResume() |
| Android | Излизане от Active | — | — | onPause() |
В iOS състоянието Active се обработва чрез UIApplicationDelegate. Основният метод — applicationDidBecomeActive(_:). Той се извиква при първото стартиране на приложението и при връщане от Inactive. Този метод е идеалното място за възобновяване на задачи, които са били спрени при прехода в Inactive: стартиране на анимации, възобновяване на таймери, рестартиране на сензори, проверка на актуализации на данни на сървъра.
От iOS 13 Apple представи UISceneDelegate за поддръжка на множество прозорци на iPad. В този случай applicationDidBecomeActive се заменя с sceneDidBecomeActive за всяка сцена. Приложенията, поддържащи само един екран, могат да продължат да използват UIApplicationDelegate. И двата подхода се извикват в момента, когато приложението или сцената стане активна.
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// Приложението стана активно — възобновяваме задачите
func applicationDidBecomeActive(_ application: UIApplication) {
resumeAnimations()
restartTimers()
refreshDataIfNeeded()
startObservingSensors()
}
// Приложението губи активност — спираме
func applicationWillResignActive(_ application: UIApplication) {
pauseAnimations()
stopTimers()
saveDraftData()
}
private func resumeAnimations() {
UIView.animate(withDuration: 0.3) {
// Възобновяване на UI анимации
}
}
private func refreshDataIfNeeded() {
let lastRefresh = UserDefaults.standard.object(forKey: "lastRefresh") as? Date ?? .distantPast
if Date().timeIntervalSince(lastRefresh) > 300 {
fetchDataFromServer()
}
}
}Кодът показва правилната обработка на Active в UIKit. applicationDidBecomeActive възобновява анимации, таймери и проверява дали е необходима актуализация на данни. applicationWillResignActive спира всичко, което може да консумира ресурси, и запазва чернови. Такава двойка методи гарантира, че приложението реагира правилно на промяна на състоянието.
В SwiftUI няма AppDelegate — управлението на състоянието става чрез Environment<ScenePhase>. Стойността .active се задава, когато сцената е на преден план и е интерактивна. SwiftUI автоматично рестартира анимациите и актуализациите при връщане в Active. Разработчикът трябва само да се абонира за onChange за изпълнение на странични ефекти.
import SwiftUI
@main
struct ActiveDemoApp: App {
@Environment(\.scenePhase) private var scenePhase
var body: some Scene {
WindowGroup {
ContentView()
}
.onChange(of: scenePhase) { oldPhase, newPhase in
switch newPhase {
case .active:
print("Сцената стана активна")
resumeWork()
case .inactive:
print("Сцената стана неактивна")
pauseWork()
case .background:
print("Сцената отиде във фонов режим")
saveState()
@unknown default:
break
}
}
}
private func resumeWork() {
// Възобновяване на мрежови заявки, анимации
}
private func pauseWork() {
// Спиране на чувствителни към време задачи
}
private func saveState() {
// Запазване на състоянието на приложението
}
}В SwiftUI scenePhase е единственият източник на истина за състоянието на приложението. onChange позволява изпълнение на действия при всеки преход. Важно е да запомните, че scenePhase е достъпен само на iOS 14+ и в SwiftUI Lifecycle. За UIKit приложения с SwiftUI екрани използвайте подхода с UIApplicationDelegate.
Active може да бъде достигнато по няколко начина. Първият и очевиден — студен старт: потребителят натиска иконата, приложението преминава от Not Running през Inactive в Active. Вторият — връщане от фон: потребителят се връща в приложението чрез App Switcher, приложението преминава през Inactive и става Active. Третият — връщане от временно прекъсване: потребителят приключва повикване, затваря Control Center или отговаря на известие — приложението се връща от Inactive в Active.
Not Running → Inactive → Active — студен старт. Background → Inactive → Active — връщане от фон. Inactive → Active — връщане от временно прекъсване. Във всеки случай applicationDidBecomeActive се извиква, но контекстът може да е различен. При студен старт преди Active се извиква didFinishLaunchingWithOptions, при връщане от фон — willEnterForeground. Разработчикът може да използва тези разлики за избор на стратегия за възстановяване на състоянието.
| Сценарий | Път на преход | Callback-и iOS | Callback-и Android |
|---|---|---|---|
| Студен старт | Not Running → Active | didFinishLaunching → didBecomeActive | onCreate → onStart → onResume |
| Връщане от фон | Background → Active | willEnterForeground → didBecomeActive | onRestart → onStart → onResume |
| Връщане от Suspended | Suspended → Active | willEnterForeground → didBecomeActive | onRestart → onStart → onResume |
| След прекъсване | Inactive → Active | didBecomeActive | onResume |
Важна забележка: при връщане от Suspended iOS не извиква didFinishLaunchingWithOptions, тъй като приложението вече е било заредено в паметта. Това означава, че инициализационният код, поставен в този метод, не се изпълнява повторно. Разработчиците често забравят за това и преместват критичната логика в applicationWillEnterForeground или applicationDidBecomeActive и за двата сценария.
В Android аналогът на Active е състоянието на Activity след извикване на onResume(). Activity се счита за активно, когато е на преден план и приема потребителски вход. Това състояние съответства на върха на стека от Activity. Ако друго Activity се появи отгоре (дори частично), текущото Activity преминава в състояние onPause — аналог на iOS Inactive.
Ключовата разлика на Android — множество Activity могат да бъдат активни едновременно в multi-window режим (split screen, freeform). В този случай Activity, с което потребителят взаимодейства, се счита за активно, а съседното — за спряно (onPause). iOS не поддържа multi-window на iPhone, само на iPad чрез UIScene.
class MainActivity : AppCompatActivity() {
override fun onResume() {
super.onResume()
// Приложението стана активно — възобновяваме задачите
resumeCameraPreview()
startLocationUpdates()
activateSensors()
}
override fun onPause() {
super.onPause()
// Приложението губи активност — освобождаваме ресурси
releaseCamera()
stopLocationUpdates()
deactivateSensors()
}
private fun resumeCameraPreview() {
// Стартиране на преглед на камерата (изисква разрешение)
cameraProvider?.unbindAll()
cameraProvider?.bindToLifecycle(
this,
cameraSelector,
preview,
imageAnalyzer
)
}
private fun startLocationUpdates() {
val locationRequest = LocationRequest.Builder(
Priority.PRIORITY_HIGH_ACCURACY, 5000
).build()
locationClient.requestLocationUpdates(
locationRequest,
locationCallback,
Looper.getMainLooper()
)
}
}Кодът показва обработката на Active в Android чрез onResume/onPause. onResume възобновява работата с камерата, геолокацията и сензорите — ресурси, които трябва да са активни само когато приложението е видимо за потребителя. onPause освобождава тези ресурси, за да не разрежда батерията. CameraX lifecycle-aware API автоматично спира прегледа при onPause.
Първо правило — не изпълнявайте тежки операции в applicationDidBecomeActive или onResume. Зареждане на данни, парсване на JSON, работа с база данни — всичко това трябва да бъде асинхронно и да не блокира основната нишка. Използвайте GCD (DispatchQueue) в iOS и Coroutines в Kotlin за фонови задачи. Основната нишка трябва само да актуализира UI и да стартира асинхронни операции.
Второ правило — синхронизирайте състоянието при всяко връщане в Active. Потребителят може да е променил настройки в системното приложение, да е получил push известие или да е актуализирал данни в друго приложение. Проверявайте актуалността на кеша при преход в Active — може би данните са остарели по време на отсъствието на потребителя.
Трето правило — не разчитайте на Active като единствено състояние. Приложението може да пропусне Active и да премине директно от Not Running в Background (ако е пуснато във фонов режим). На iOS това се случва при стартиране чрез push известие с опция content-available. На Android — при стартиране чрез BroadcastReceiver. Винаги проверявайте текущото състояние преди изпълнение на UI операции.
Четвърто правило — използвайте Activity Result API на Android вместо onActivityResult. Това позволява обработка на резултата от извикване на камера, галерия или разрешения директно в състояние Active без загуба на данни при повторно създаване на Activity. За iOS използвайте async/await с UIApplication.shared.open за системни диалози.
import UIKit
final class ActiveStateManager {
static let shared = ActiveStateManager()
private var isActive = false
func setActive(_ active: Bool) {
isActive = active
if active {
NotificationCenter.default.post(name: .appDidBecomeActive, object: nil)
}
}
func performWhenActive(_ block: @escaping () -> Void) {
if isActive {
block()
} else {
// Отложи изпълнението до връщане в Active
NotificationCenter.default.addObserver(
forName: .appDidBecomeActive,
object: nil,
queue: .main
) { _ in
block()
}
}
}
}
extension Notification.Name {
static let appDidBecomeActive = Notification.Name("appDidBecomeActive")
}Кодът показва мениджър на състояние Active, който позволява на други компоненти на приложението да проверяват текущото активно състояние. performWhenActive или изпълнява блока незабавно (ако приложението е активно), или отлага изпълнението до връщане в Active. Това е полезно за услуги, които трябва да изпълнят действие, след като потребителят се върне в приложението.
Често задавани въпроси
Методът се извиква всеки път, когато приложението преминава в активно състояние: при първо стартиране, при връщане от фон, след затваряне на Control Center или Notification Center, след приключване на повикване. В нормална сесия може да бъде извикан 5–10 пъти в зависимост от действията на потребителя. Не поставяйте еднократна инициализация в този метод.
Visible — неофициален термин, означаващ, че приложението е видимо на екрана, но може да не приема събития (например частично покрито от друг прозорец на iPad). Active — официално състояние, в което приложението е едновременно видимо и интерактивно. На iPhone Visible приложението винаги е Active, на iPad е възможна ситуация Visible + Inactive.
willEnterForeground се извиква при връщане от фон, но приложението все още не е активно — то е в Inactive. didBecomeActive се извиква, след като приложението е станало напълно интерактивно. Ако трябва да изпълните действие, преди потребителят да види интерфейса — използвайте willEnterForeground. Ако след показване — didBecomeActive.
Не. Active предполага, че приложението е на преден план и се показва на екрана. Без видим UI приложението може да бъде в Background или Suspended. Изключение — iPad multi-window, където един прозорец може да бъде активен, а друг не, но и двата са видими. VoiceOver и диктофон не променят това правило.
На iOS симулатора натиснете Cmd+Shift+H за отиване на началния екран (приложението преминава в Background), след това кликнете отново върху иконата на приложението. Използвайте Cmd+L за заключване на екрана (willResignActive) и отключване (didBecomeActive). За тестване на Inactive извикайте Control Center (Cmd+Shift+; за macOS клавиатура) или Notification Center.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също