Background Service là một thành phần Android được thiết kế để thực hiện các thao tác dài trong nền mà không có giao diện người dùng. Không giống Activity, Service tiếp tục hoạt động ngay cả sau khi ứng dụng được thu nhỏ hoặc người dùng chuyển sang ứng dụng khác. Theo Android Developers, 2026, có ba loại dịch vụ: Started Service, Bound Service và Foreground Service, mỗi loại có vòng đời và phạm vi ứng dụng riêng.
Điểm chính
Background Service (hay đơn giản là Service) là một trong bốn thành phần cơ bản của ứng dụng Android, cùng với Activity, BroadcastReceiver và ContentProvider. Không giống Activity, Service không có giao diện trực quan và được thiết kế để thực hiện các thao tác phải tiếp tục bất kể ứng dụng có ở tiền cảnh hay không.
Service chạy trên luồng chính của ứng dụng, vì vậy mọi thao tác chặn bên trong nó đều yêu cầu tạo một luồng riêng. Nếu không làm điều này, hệ thống sẽ gửi ANR (Application Not Responding). Đối với các thao tác nền đơn giản, Android cung cấp IntentService, tự động tạo một luồng công việc. Trong các dự án hiện đại, nên sử dụng coroutine Kotlin với CoroutineScope bên trong Service để xử lý bất đồng bộ mà không chặn luồng chính.
Mục đích chính của Service là phát nhạc, tải tệp, xử lý yêu cầu mạng, đồng bộ dữ liệu và các tác vụ khác phải tiếp tục sau khi người dùng rời ứng dụng. Tuy nhiên, từ Android 8, các nhà phát triển phải lựa chọn cẩn thận giữa các loại dịch vụ, tính đến các giới hạn của công việc nền.
Service có vòng đời riêng, khác với Activity. Nó bao gồm bốn phương thức chính: onCreate, onStartCommand, onBind và onDestroy. Hiểu được chu trình này là điều cần thiết để triển khai các tác vụ nền mà không bị rò rỉ bộ nhớ.
Phương thức onCreate được gọi khi dịch vụ được tạo, một lần trong suốt vòng đời của nó. Tại đây, các tài nguyên như bộ đếm thời gian, kết nối cơ sở dữ liệu và socket được khởi tạo. Phương thức onStartCommand được gọi mỗi khi startService được gọi, cho phép gửi lệnh đến một dịch vụ đang chạy. Giá trị trả về xác định hành vi của hệ thống khi khởi động lại.
class DownloadService : Service() {
override fun onCreate() {
super.onCreate()
initializeDownloader()
}
override fun onStartCommand(
intent: Intent?,
flags: Int,
startId: Int
): Int {
downloadFile(intent?.getStringExtra("url"))
return START_STICKY
}
override fun onBind(intent: Intent): IBinder? = null
}
onBind được gọi khi liên kết một dịch vụ qua bindService và trả về một đối tượng IBinder để tương tác với client. Phương thức này chỉ được sử dụng cho Bound Service. onDestroy là lệnh gọi cuối cùng trước khi dịch vụ bị hủy. Tại đây, mọi tài nguyên được giải phóng, luồng được dừng và tác vụ bị hủy.
Android cung cấp ba loại Service, mỗi loại được thiết kế cho kịch bản riêng. Chọn sai loại có thể dẫn đến hành vi ứng dụng không ổn định hoặc tiêu hao pin.
Started Service được khởi chạy bằng cách gọi startService và chạy cho đến khi gọi stopSelf hoặc stopService. Nó phù hợp với các tác vụ cần thực hiện ngay lập tức: gửi phân tích, xử lý hình ảnh, tải một tệp duy nhất. Sau khi hoàn thành công việc, dịch vụ tự dừng.
Bound Service cung cấp giao diện client-server, cho phép Activity, Fragment hoặc thành phần khác tương tác với dịch vụ. Dịch vụ tồn tại chừng nào có ít nhất một client liên kết. Khi tất cả client hủy liên kết, dịch vụ bị hủy. Bound Service hữu ích cho các tác vụ yêu cầu giao tiếp hai chiều: trình phát nhạc, dẫn đường.
Foreground Service là Started Service có thông báo liên tục trên thanh trạng thái. Hệ thống coi dịch vụ như vậy là đang hoạt động và không tiêu diệt nó ngay cả khi thiếu bộ nhớ. Foreground Service là bắt buộc đối với phát nhạc, ghi âm, theo dõi vị trí và các tác vụ quan trọng khác cho người dùng.
| Tham số | Started | Bound | Foreground |
|---|---|---|---|
| Khởi động | startService | bindService | startForeground |
| Tuổi thọ | đến stopSelf | chừng nào còn client | đến stopForeground |
| Thông báo | không | không | bắt buộc |
| Có thể bị tiêu diệt | có | có | không |
| Ví dụ | tải xuống | trình phát | nhạc |
Tạo một dịch vụ bắt đầu bằng khai báo một lớp kế thừa từ Service và đăng ký nó trong AndroidManifest.xml. Không có đăng ký trong tệp kê khai, hệ thống sẽ không thể khởi động dịch vụ và mọi lệnh gọi startService sẽ gây ra ngoại lệ.
// Đăng ký trong AndroidManifest.xml
@SuppressLint("ForegroundServiceType")
class SyncService : Service() {
override fun onStartCommand(
intent: Intent?,
flags: Int,
startId: Int
): Int {
startForeground(
NOTIFICATION_ID,
createNotification()
)
performSync(intent)
return START_NOT_STICKY
}
}
Để khởi động một dịch vụ từ Activity hoặc Fragment, một Intent với tham chiếu rõ ràng đến lớp dịch vụ được sử dụng. Từ Android 8, Foreground Service yêu cầu quyền FOREGROUND_SERVICE trong tệp kê khai.
// Khởi động Started Service
val intent = Intent(this, SyncService::class.java)
intent.putExtra("action", "sync")
startService(intent)
// Khởi động Foreground Service (Android 8+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent)
} else {
startService(intent)
}
Từ Android 8 (API 26), Google đã đưa ra các giới hạn nghiêm ngặt đối với các dịch vụ nền. Việc khởi động dịch vụ nền (khi ứng dụng không ở tiền cảnh) chỉ được phép trong các trường hợp ngoại lệ: khi nhận thông báo push, sau khi thiết bị khởi động hoặc qua JobScheduler.
Đối với các tác vụ dài không cần thực hiện ngay, nên sử dụng WorkManager hoặc JobScheduler. Nếu một ứng dụng thực sự cần một dịch vụ đang chạy, cách duy nhất là Foreground Service với thông báo mà người dùng có thể thấy. Việc khởi động một dịch vụ không có thông báo trong nền sẽ bị hệ thống bỏ qua.
JobIntentService là một lớp chuyên dụng xuất hiện trong thư viện hỗ trợ để hoạt động trên Android 5+. Nó kết hợp hành vi của IntentService (luồng công việc tự động, xử lý tuần tự) với lập lịch qua JobScheduler. Trên Android 8+, JobIntentService sử dụng JobScheduler nội bộ, và trên các phiên bản cũ hơn, một Service thông thường. Điều này cho phép xử lý thống nhất các tác vụ nền mà không cần kiểm tra phiên bản Android bổ sung.
class UploadJobService : JobIntentService() {
companion object {
private const val JOB_ID = 1000
fun enqueueWork(context: Context, work: Intent) {
enqueueWork(
context,
UploadJobService::class.java,
JOB_ID,
work
)
}
}
override fun onHandleWork(intent: Intent) {
val fileUri = intent.getStringExtra("file_uri")
// Chạy trong một luồng nền
uploadFile(fileUri)
}
}
Một trong những vấn đề thường gặp khi làm việc với Background Service là rò rỉ bộ nhớ. Vì Service có thể sống lâu hơn Activity, các tham chiếu đến Activity bên trong Service (qua listener, callback hoặc broadcast) ngăn việc thu gom rác của các thành phần UI. Nên sử dụng WeakReference, ViewModel hoặc LiveData để giao tiếp Service với UI. Trong onDestroy, nhớ hủy tất cả đăng ký, dừng luồng và đóng con trỏ.
Sự lựa chọn giữa Background Service và WorkManager phụ thuộc vào kịch bản. Service phù hợp cho các tác vụ phải chạy ngay lập tức và liên tục: phát nhạc, ghi âm, theo dõi GPS. WorkManager tốt hơn cho các tác vụ trì hoãn, được đảm bảo: đồng bộ, gửi phân tích, tải log. WorkManager sống sót qua khởi động lại thiết bị, còn Service thì không. Service có thể là Foreground với thông báo, trong khi WorkManager lặng lẽ trong nền. Trong thực tế, các nhà phát triển kết hợp cả hai: Foreground Service cho các tác vụ quan trọng với người dùng và WorkManager cho bảo trì nền.
Android 12 đã giới thiệu cờ android:foregroundServiceType, yêu cầu chỉ định loại dịch vụ: dataSync, camera, connectedDevice, location, mediaPlayback và các loại khác. Chỉ định sai loại sẽ gây ra ngoại lệ khi khởi động. Thực hành này làm cho Background Service minh bạch hơn cho cả người dùng và hệ thống.
Đăng ký Service đúng cách trong tệp kê khai bao gồm thuộc tính exported (khả năng truy cập cho ứng dụng bên ngoài), foregroundServiceType (loại dịch vụ nền trên Android 12+) và quyền. Đối với Bound Service, bạn cũng cần khai báo android:permission="android.permission.BIND_JOB_SERVICE" cho JobIntentService. Không có đăng ký trong tệp kê khai, mọi lệnh gọi startService hoặc bindService sẽ gây ra ngoại lệ, vì vậy kiểm tra tệp kê khai là bước đầu tiên khi chẩn đoán sự cố liên quan đến Service.
Câu hỏi thường gặp
Service chạy trên luồng chính (UI Thread) của ứng dụng. Mọi thao tác chặn bên trong onStartCommand hoặc onHandleIntent phải được đưa vào một luồng riêng hoặc coroutine, nếu không hệ thống sẽ gửi ANR sau 5 giây.
IntentService là một lớp con của Service tự động tạo một luồng công việc và xử lý các lệnh tuần tự. Sau khi hoàn thành tác vụ cuối cùng, IntentService tự dừng. Từ Android 8, IntentService được coi là lỗi thời để chuyển sang JobIntentService hoặc WorkManager.
Khởi động Started Service từ nền trên Android 12 bị cấm. Ngoại lệ là Foreground Service với foregroundServiceType được khai báo trong tệp kê khai và thông báo hợp lệ. Việc khởi động ngắn sau khi nhận tin nhắn FCM ưu tiên cao cũng được cho phép.
Có ba phương pháp: BroadcastReceiver với broadcast cục bộ, cơ chế Messenger qua Handler, và LiveData/Flow trong kiến trúc MVVM với ViewModel dùng chung. Đối với Bound Service, IBinder được sử dụng với lời gọi phương thức trực tiếp.
Nếu Service được khởi động với cờ START_STICKY, hệ thống sẽ khởi động lại nó sau khi tiến trình bị tiêu diệt vì thiếu bộ nhớ. Cờ START_NOT_STICKY có nghĩa là hệ thống sẽ không khởi động lại dịch vụ. START_REDELIVER_INTENT tương tự START_STICKY nhưng gửi Intent cuối cùng.
Tổng kế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