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 โต้ตอบได้อย่างสมบูรณ์ และตอบสนองต่อท่าทาง การเลื่อน การกด และแอนิเมชัน
ระบบให้ลำดับความสำคัญสูงสุดด้าน CPU และ RAM แก่แอปพลิเคชันใน Active ซึ่งหมายความว่าระบบจะไม่ยุติแอปพลิเคชันดังกล่าวเมื่อทรัพยากรไม่เพียงพอ — กระบวนการพื้นหลังและที่ถูกระงับจะถูกปล่อยออกก่อน อย่างไรก็ตาม แอปพลิเคชันควรใช้ทรัพยากรอย่างมีประสิทธิภาพเพื่อไม่ให้แบตเตอรี่หมดและไม่ทำให้เกิดการจำกัดความเร็ว 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 ที่ผู้ใช้โต้ตอบด้วยถือว่าแอคทีฟ ส่วน 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 เพื่อไปยัง Home Screen (แอปพลิเคชันไปที่ Background) จากนั้นคลิกที่ไอคอนแอปพลิเคชันอีกครั้ง ใช้ Cmd+L เพื่อล็อกหน้าจอ (willResignActive) และปลดล็อก (didBecomeActive) ในการทดสอบ Inactive ให้เรียก Control Center (Cmd+Shift+; สำหรับคีย์บอร์ด macOS) หรือ Notification Center
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม