Active——iOS应用生命周期的活跃状态,应用处于前台,接收触摸事件并与用户交互。我们来了解Active状态如何工作,UIApplicationDelegate的哪些方法负责它,以及如何在Swift中正确处理Active和Inactive之间的转换。
要点
Active——移动应用生命周期的一种状态,应用处于前台,显示在设备屏幕上,并与用户积极交互。在此状态下,应用接收所有触摸事件、按键、加速度计和陀螺仪数据,并拥有对图形处理器的完全访问权限以渲染界面。
在iOS上,Active状态是五状态生命周期模型的一部分:Not Running → Inactive → Active → Inactive → Background → Suspended → Not Running。在Android上,它的对应状态是调用onResume之后的Activity状态,此时Activity位于堆栈顶部并接收用户输入。Active是唯一一个UI完全可交互并对手势、滚动、点击和动画做出反应的状态。
系统为处于Active状态的应用提供最高的处理器和RAM优先级。这意味着系统在资源不足时不会终止这样的应用——首先会卸载后台和挂起的进程。然而,应用应该有效利用资源,以免耗尽电池或导致CPU节流。
对用户来说,Active是使用应用的正常状态。用户可以看到界面,可以按下按钮、填写表单、滚动信息流。任何中断此状态的行为(通话、通知、向上滑动打开Control Center)都会将应用切换到Inactive,之后它可以返回Active或进入Background。
iOS使用UIApplicationMain来管理状态。在过渡到Active时,系统调用applicationDidBecomeActive。对于SwiftUI,类似的机制是通过Environment观察scenePhase。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()
}
}
}代码展示了UIKit中Active的正确处理。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中可用。对于带有SwiftUI屏幕的UIKit应用,请使用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的对应状态是调用onResume()之后的Activity状态。Activity在处于前台并接收用户输入时被视为活跃。此状态对应于Activity堆栈的顶部。如果另一个Activity出现在其上方(即使部分覆盖),当前Activity将进入onPause状态——相当于iOS的Inactive。
Android的关键区别——在multi-window模式下(分屏、自由形式),多个Activity可以同时活跃。在这种情况下,用户正在交互的Activity被视为活跃,相邻的Activity被暂停(onPause)。iOS在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()
)
}
}代码展示了在Android中通过onResume/onPause处理Active。onResume恢复相机工作、地理定位和传感器——这些资源只应在应用对用户可见时保持活跃。onPause释放这些资源以免消耗电池。CameraX生命周期感知API会在onPause时自动停止预览。
第一条规则——不要在applicationDidBecomeActive或onResume中执行重操作。加载数据、解析JSON、处理数据库——所有这些都应该是异步的,不阻塞主线程。在iOS中使用GCD(DispatchQueue),在Kotlin中使用Coroutines处理后台任务。主线程只应更新UI和启动异步操作。
第二条规则——在每次返回到Active时同步状态。用户可能已经更改了系统应用的设置、收到了推送通知或在其他应用中更新了数据。在过渡到Active时检查缓存的时效性——数据可能在用户离开期间已过时。
第三条规则——不要仅依赖Active作为唯一状态。应用可能跳过Active并直接从Not Running进入Background(如果在后台启动)。在iOS中,这发生在通过带有content-available选项的推送通知启动时。在Android中——通过BroadcastReceiver启动时。在执行UI操作之前始终检查当前状态。
第四条规则——在Android中使用Activity Result API代替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(对于macOS键盘使用Cmd+Shift+;)或Notification Center。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。