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-а. То значи да систем неће прекинути такву апликацију при недостатку ресурса — прво ће бити истоварени позадински и суспендовани процеси. Међутим, апликација треба ефикасно да користи ресурсе како не би празнила батерију и изазивала тротлинг процесора.
За корисника 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. Програмер може користити ове разлике за избор стратегије обнављања стања.
| Сценарио | Пут прелаза | Повратни позиви iOS | Повратни позиви 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. Корисник је могао да промени подешавања у системској апликацији, прими пусх обавештење или ажурира податке у другој апликацији. Проверавајте актуелност кеша при преласку у Active — можда су подаци застарели током одсуства корисника.
Треће правило — не ослањајте се на Active као једино стање. Апликација може прескочити Active и прећи директно из Not Running у Background (ако је покренута у позадини). На iOS-у се то дешава при покретању путем пусх обавештења са опцијом 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. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође