Background Task là một cơ chế iOS cho phép ứng dụng hoàn thành một thao tác sau khi chuyển sang chế độ nền. Hệ thống cấp thời gian giới hạn (lên đến 30 giây) để thực thi tác vụ, sau đó ứng dụng sẽ bị tạm dừng cưỡng bức. Theo Apple, 2026, việc sử dụng expiration handler là yêu cầu bắt buộc để hoàn thành tác vụ nền một cách chính xác.
Những điểm chính
Background Task là cơ chế lập trình iOS cho phép ứng dụng kéo dài thời gian thực thi sau khi chuyển sang nền. Khi người dùng thu nhỏ ứng dụng, hệ thống gọi UIApplicationDelegate.applicationDidEnterBackground. Nếu có thao tác quan trọng đang được thực hiện tại thời điểm đó (lưu dữ liệu, gửi yêu cầu), ứng dụng có thể yêu cầu thêm thời gian qua beginBackgroundTask.
Nếu không sử dụng Background Task, ứng dụng sẽ bị tạm dừng cưỡng bức trong vòng 3–5 giây sau khi vào nền. Tất cả các thao tác chưa hoàn thành đều bị gián đoạn: dữ liệu không được lưu, yêu cầu mạng bị hủy bỏ, trạng thái bị mất. Background Task cấp cho ứng dụng tới 30 giây để hoàn thành các thao tác này một cách chính xác.
beginBackgroundTask xuất hiện trong iOS 4.0 — bản phát hành đầu tiên hỗ trợ đa nhiệm. Trước iOS 4, ứng dụng bị kết thúc hoàn toàn khi nhấn nút Home. iOS 7 giới thiệu Background Fetch và cấu hình nền URLSession. Bắt đầu từ iOS 13, Apple khuyến nghị BGTaskScheduler cho các dự án mới, nhưng beginBackgroundTask vẫn phù hợp cho các thao tác ngắn hạn.
beginBackgroundTask là phương thức UIApplication đăng ký một tác vụ và trả về UIBackgroundTaskIdentifier duy nhất. Hệ thống tăng bộ đếm thời gian thực thi nền. Khi hết thời gian, expiration handler được gọi, sau đó ứng dụng phải gọi endBackgroundTask(identifier:) để hoàn thành tác vụ chính xác.
Bước 1 — ứng dụng nhận thông báo didEnterBackground. Bước 2 — beginBackgroundTask(expirationHandler:) được gọi. Bước 3 — thao tác quan trọng (lưu, yêu cầu mạng) được thực hiện. Bước 4 — khi hoàn thành, endBackgroundTask(identifier:) được gọi. Nếu thao tác không hoàn thành trong 30 giây — hệ thống gọi expiration handler, và ứng dụng phải kết thúc tác vụ ngay lập tức.
var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
func startBackgroundTask() {
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
// Expiration handler
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
}
func finishBackgroundTask() {
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
Thuộc tính backgroundTimeRemaining trả về số giây còn lại trước khi tác vụ nền bị kết thúc cưỡng bức. Giá trị giảm dần theo thời gian thực. Nếu ứng dụng không ở chế độ nền — nó trả về DBL_MAX. Sử dụng thuộc tính này để điều chỉnh hành vi: khi còn dưới 5 giây, hãy ngắt các thao tác dài và lưu tiến trình.
Một ứng dụng có thể đăng ký nhiều Background Tasks cùng lúc. Mỗi tác vụ nhận được mã định danh riêng. Tổng thời gian thực thi được cộng dồn — nếu 3 tác vụ được đăng ký, ứng dụng có thể nhận được tới 90 giây. Tuy nhiên, hệ thống có thể kết thúc ứng dụng sớm hơn nếu tài nguyên cạn kiệt hoặc vượt quá giới hạn pin.
Expiration handler là khối mã mà hệ thống gọi khi hết thời gian được cấp cho tác vụ nền. Có expiration handler là yêu cầu bắt buộc của Apple. Nếu không có, ứng dụng có thể bị kết thúc cưỡng bức bởi hệ thống và dữ liệu có thể bị mất.
Expiration handler phải thực hiện các hành động tối thiểu để lưu trạng thái ứng dụng: lưu dữ liệu hiện tại vào bộ nhớ liên tục, gọi endBackgroundTask với mã định danh tương ứng và đặt mã định danh thành .invalid. Bên trong expiration handler, không được phép bắt đầu các thao tác dài mới — thời gian thực thi bị giới hạn ở 1–2 giây.
func handleExpiration() {
// Đang lưu tiến trình thực thi
saveProgressToUserDefaults()
// Đang hủy các yêu cầu mạng đang hoạt động
currentTask?.cancel()
// Đang kết thúc Background Task
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
// Đăng ký với expiration handler
backgroundTaskID = UIApplication.shared.beginBackgroundTask(
withName: "SaveDocument",
expirationHandler: handleExpiration
)
Lỗi phổ biến nhất là không gọi endBackgroundTask bên trong expiration handler. Trong trường hợp này, hệ thống tiếp tục coi tác vụ là đang hoạt động, ứng dụng không chuyển sang trạng thái Suspended và pin bị hao kiệt. Lỗi thứ hai là bắt đầu các thao tác dài bên trong expiration handler. Hệ thống có thể kết thúc ứng dụng trước khi chúng hoàn thành và dữ liệu sẽ bị mất.
Trong expiration handler, thời gian thực thi bị giới hạn nghiêm trọng — thường là dưới 1 giây. Do đó, bên trong handler bạn không thể thực hiện: tính toán phức tạp, yêu cầu mạng, ghi lượng lớn dữ liệu vào CoreData. Chỉ các thao tác nguyên tử: ghi một khóa vào UserDefaults, đặt cờ trạng thái, gọi endBackgroundTask.
Hãy xem xét một triển khai Background Task hoàn chỉnh để lưu tài liệu khi ứng dụng vào nền. Ví dụ bao gồm đăng ký tác vụ, thực hiện thao tác với kiểm tra thời gian còn lại và hoàn thành chính xác qua endBackgroundTask.
Khi vào nền, ứng dụng bắt đầu lưu một tài liệu lớn. Background Task cấp tới 30 giây cho thao tác. Nếu hết thời gian — expiration handler lưu kết quả trung gian. Sau khi lưu hoàn tất, endBackgroundTask được gọi để giải phóng tài nguyên.
class DocumentManager {
private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
func saveDocumentInBackground(document: Document) {
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
self.saveProgress(document)
self.endBackgroundTask()
}
DispatchQueue.global().async {
document.save()
self.endBackgroundTask()
}
}
private func endBackgroundTask() {
UIApplication.shared.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
}
Yêu cầu mạng trong nền đòi hỏi sự chú ý đặc biệt — expiration handler có thể ngắt yêu cầu trước khi nhận được phản hồi. Sử dụng URLSession với dataTask và kiểm tra backgroundTimeRemaining trước khi gửi. Nếu còn dưới 5 giây — hãy hoãn gửi cho đến lần đánh thức ứng dụng tiếp theo.
func sendCriticalData(data: Data) {
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
// Đang lưu dữ liệu để gửi lại
saveForRetry(data)
self.endBackgroundTask()
}
let task = URLSession.shared.dataTask(with: request) { _, _, error in
if error != nil {
saveForRetry(data)
}
self.endBackgroundTask()
}
task.resume()
}
Đối với các thao tác dài, hãy giám sát UIApplication.shared.backgroundTimeRemaining. Nếu giá trị giảm xuống dưới ngưỡng (10 giây), hãy ngắt thao tác hiện tại và bắt đầu lưu kết quả trung gian. Điều này cho phép hoàn thành chính xác trước khi expiration handler bị gọi cưỡng bức.
Background Task là cơ chế mạnh mẽ, nhưng sử dụng không đúng cách dẫn đến các vấn đề về pin và hiệu năng. Tuân theo các khuyến nghị của Apple và các phương pháp đã được cộng đồng kiểm chứng sẽ giúp tránh các lỗi phổ biến và đảm bảo hoạt động ổn định của ứng dụng.
Hoàn thành Background Task càng nhanh càng tốt. Mỗi giây thực thi trong nền đều tiêu hao pin. Thời gian tối ưu — dưới 5 giây. Nếu thao tác mất nhiều thời gian hơn — hãy cân nhắc sử dụng BGTaskScheduler hoặc Background Modes. Đừng kéo dài tác vụ một cách giả tạo qua các lời gọi beginBackgroundTask mà không có nhu cầu thực sự.
Lưu trữ UIBackgroundTaskIdentifier như một thuộc tính của lớp hoặc cấu trúc. Không bao giờ truyền nó như một biến toàn cục — điều này dẫn đến xung đột với nhiều tác vụ. Đặt mã định danh thành .invalid sau khi gọi endBackgroundTask để ngăn chặn kết thúc kép.
Trong Xcode Simulator, chuyển ứng dụng sang nền qua nút Home (Command + Shift + H). Sử dụng Debug — Simulate Background Fetch để kiểm thử đánh thức nền. Để mô phỏng hết thời gian, đặt Biến môi trường BACKGROUND_TASK_SIMULATE_EXPIRATION = YES — expiration handler sẽ được gọi trong vòng 5 giây.
// Đang kiểm tra thời gian còn lại trước khi bắt đầu thao tác
let remaining = UIApplication.shared.backgroundTimeRemaining
guard remaining > 10.0 else {
// Không đủ thời gian — hoãn tác vụ
scheduleForNextLaunch()
return
}
// Đang thực thi thao tác với kiểm soát thời gian
backgroundTaskID = UIApplication.shared.beginBackgroundTask {
saveProgress()
self.endBackgroundTask()
}
Expiration handler capture self — điều này có thể tạo vòng lặp retain nếu backgroundTaskID được lưu trữ như thuộc tính của cùng một đối tượng. Sử dụng [weak self] trong closure hoặc lưu trữ mã định danh riêng biệt. Vòng lặp retain dẫn đến rò rỉ bộ nhớ — đối tượng sẽ không được giải phóng cho đến khi ứng dụng kết thúc.
Với bản phát hành iOS 13, Apple đã giới thiệu BGTaskScheduler — sự thay thế hiện đại cho API Background Task cũ. Cả hai cơ chế đều giải quyết các tác vụ tương tự, nhưng BGTaskScheduler cung cấp nhiều kiểm soát và linh hoạt hơn. Hiểu được sự khác biệt sẽ giúp chọn công cụ phù hợp cho một kịch bản cụ thể.
| Đặc điểm | Background Task | BGTaskScheduler |
|---|---|---|
| Phiên bản iOS | 4.0+ | 13.0+ |
| Thời gian tối đa | 30 giây | 30 giây / 10 phút (processing) |
| Khởi tạo | didEnterBackground | Lập lịch + hệ thống |
| Đảm bảo thực thi | Chỉ khi vào nền | Bất kỳ lúc nào hệ thống thuận tiện |
| Expiration handler | Có, bắt buộc | Có, qua task.expirationHandler |
| Internet | Yêu cầu khi khởi động | Có sẵn qua requiresNetworkConnectivity |
| Sạc | Không yêu cầu | Tùy chọn cho processing |
beginBackgroundTask phù hợp cho các thao tác ngắn hạn cần được thực thi ngay lập tức khi vào nền: lưu trạng thái, hoàn thành yêu cầu mạng, lưu dữ liệu vào cache. API đơn giản và không yêu cầu cấu hình Info.plist hoặc đăng ký mã định danh. Lý tưởng cho các thao tác mất dưới 10 giây.
BGTaskScheduler phù hợp cho các tác vụ có thể được thực thi bất kỳ lúc nào thuận tiện: đồng bộ định kỳ, dọn dẹp cache, cập nhật widget. Hệ thống tự chọn thời điểm tối ưu dựa trên hành vi người dùng và trạng thái pin. Đối với các thao tác dài (lên đến 10 phút), hãy sử dụng BGProcessingTask.
Cả hai API có thể được sử dụng trong cùng một ứng dụng. Background Task — cho các thao tác tức thời khi vào nền (lưu bản nháp). BGTaskScheduler — cho các cập nhật theo lịch trình (đồng bộ mỗi 6 giờ). Phân chia trách nhiệm đảm bảo bảo toàn dữ liệu chính xác và bảo trì nền tiết kiệm năng lượng.
Câu hỏi thường gặp
Có, beginBackgroundTask có thể được gọi tại bất kỳ thời điểm nào trong quá trình thực thi ứng dụng. Tuy nhiên, việc cấp phát thời gian của hệ thống sẽ chỉ bắt đầu sau khi chuyển sang nền. Nếu ứng dụng ở tiền cảnh, gọi beginBackgroundTask không có hiệu lực — backgroundTimeRemaining trả về DBL_MAX và tác vụ sẽ được kích hoạt khi vào nền.
Nếu endBackgroundTask không được gọi, hệ thống tiếp tục coi ứng dụng đang hoạt động trong nền. Sau 30 giây, expiration handler sẽ được gọi, nhưng nếu nó cũng thiếu endBackgroundTask — ứng dụng ở lại trong bộ nhớ, hao pin. Trong iOS 13+, hệ thống kết thúc cưỡng bức ứng dụng đó sau 3 phút.
Thời gian tiêu chuẩn là 30 giây. Chỉ có thể kéo dài qua Background Modes: Âm thanh (phát lại), Vị trí (định vị), Bluetooth (BLE). Hoặc qua BGProcessingTask (iOS 13+) — lên đến 10 phút với sạc và Wi-Fi. beginBackgroundTask tự nó không cung cấp cách để tăng giới hạn.
Có, iPadOS hỗ trợ đầy đủ beginBackgroundTask với các giới hạn tương tự như iOS. Trên iPad với Stage Manager, ứng dụng có thể ở lại bộ nhớ lâu hơn — hệ thống tạm dừng ứng dụng ít thường xuyên hơn do RAM lớn hơn. Nhưng giới hạn 30 giây cho tác vụ nền vẫn được giữ nguyên.
Kết nối thiết bị với Xcode, khởi chạy ứng dụng, thu nhỏ nó — nhật ký hệ thống sẽ xuất hiện trong console. Sử dụng sysdiagnose để thu thập nhật ký chi tiết: kích hoạt cưỡng bức sysdiagnose từ thiết bị (Tăng âm lượng + Giảm âm lượng + Nguồn). Trong Xcode Debug Navigator, giám sát hoạt động của các tác vụ nền.
Tóm tắt
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm