Có 2 bước để chạy một dịch vụ trên nền trước từ ứng dụng của bạn. Trước tiên, bạn phải bắt đầu dịch vụ bằng cách gọi context.startForegroundService()
. Sau đó, hãy để dịch vụ gọi ServiceCompat.startForeground()
để tự chuyển thành dịch vụ trên nền trước.
Điều kiện tiên quyết
Tuỳ thuộc vào cấp độ API mà ứng dụng của bạn nhắm đến, có một số hạn chế về thời điểm ứng dụng có thể chạy một dịch vụ trên nền trước.
Các ứng dụng nhắm đến Android 12 (API cấp 31) trở lên không được phép bắt đầu một dịch vụ trên nền trước trong khi ứng dụng ở chế độ nền, ngoại trừ một số trường hợp cụ thể. Để biết thêm thông tin và thông tin về các trường hợp ngoại lệ đối với quy tắc này, hãy xem bài viết Các hạn chế khi khởi động dịch vụ trên nền trước từ nền.
Những ứng dụng nhắm đến Android 14 (API cấp 34) trở lên phải yêu cầu các quyền thích hợp cho loại dịch vụ trên nền trước. Khi ứng dụng cố gắng chuyển một dịch vụ lên nền trước, hệ thống sẽ kiểm tra các quyền thích hợp và gửi
SecurityException
nếu ứng dụng thiếu quyền nào. Ví dụ: nếu bạn cố gắng chạy một dịch vụ trên nền trước thuộc loạilocation
, hệ thống sẽ kiểm tra để đảm bảo ứng dụng của bạn đã có quyềnACCESS_COARSE_LOCATION
hoặcACCESS_FINE_LOCATION
. Tài liệu về loại dịch vụ trên nền trước liệt kê các điều kiện tiên quyết bắt buộc đối với từng loại dịch vụ trên nền trước.
Ra mắt dịch vụ
Để khởi chạy một dịch vụ trên nền trước, trước tiên, bạn phải khởi chạy dịch vụ đó dưới dạng một dịch vụ thông thường (không phải dịch vụ trên nền trước):
Kotlin
val intent = Intent(...) // Build the intent for the service context.startForegroundService(intent)
Java
Context context = getApplicationContext(); Intent intent = new Intent(...); // Build the intent for the service context.startForegroundService(intent);
Các điểm chính về mã
- Đoạn mã này khởi chạy một dịch vụ. Tuy nhiên, dịch vụ này chưa chạy ở nền trước. Trong chính dịch vụ này, bạn cần gọi
ServiceCompat.startForeground()
để chuyển dịch vụ thành dịch vụ trên nền trước.
Đưa một dịch vụ lên nền trước
Sau khi một dịch vụ đang chạy, bạn cần gọi ServiceCompat.startForeground()
để yêu cầu dịch vụ chạy ở nền trước. Thông thường, bạn sẽ gọi phương thức này trong phương thức onStartCommand()
của dịch vụ.
ServiceCompat.startForeground()
lấy các thông số sau:
- Dịch vụ.
- Một số nguyên dương xác định riêng biệt thông báo của dịch vụ trong thanh trạng thái.
- Chính đối tượng
Notification
. - Loại hoặc các loại dịch vụ trên nền trước xác định công việc mà dịch vụ thực hiện
Các loại dịch vụ trên nền trước mà bạn truyền đến startForeground()
các loại được khai báo trong tệp kê khai, tuỳ thuộc vào trường hợp sử dụng cụ thể. Sau đó, nếu cần thêm các loại dịch vụ khác, bạn có thể gọi lại startForeground()
.
Ví dụ: giả sử một ứng dụng thể dục chạy dịch vụ theo dõi hoạt động chạy bộ luôn cần thông tin location
, nhưng có thể cần hoặc không cần phát nội dung nghe nhìn. Bạn cần khai báo cả location
và mediaPlayback
trong tệp kê khai. Nếu người dùng bắt đầu chạy và chỉ muốn theo dõi vị trí của họ, thì ứng dụng của bạn nên gọi startForeground()
và chỉ truyền quyền ACCESS_FINE_LOCATION
. Sau đó, nếu người dùng muốn bắt đầu phát âm thanh, hãy gọi lại startForeground()
và truyền tổ hợp theo bit của tất cả các loại dịch vụ trên nền trước (trong trường hợp này là ACCESS_FINE_LOCATION|FOREGROUND_SERVICE_MEDIA_PLAYBACK
).
Ví dụ sau đây cho thấy mã mà một dịch vụ camera sẽ dùng để tự quảng bá thành dịch vụ trên nền trước:
Kotlin
class MyCameraService: Service() { private fun startForeground() { // Before starting the service as foreground check that the app has the // appropriate runtime permissions. In this case, verify that the user has // granted the CAMERA permission. val cameraPermission = PermissionChecker.checkSelfPermission(this, Manifest.permission.CAMERA) if (cameraPermission != PermissionChecker.PERMISSION_GRANTED) { // Without camera permissions the service cannot run in the foreground // Consider informing user or updating your app UI if visible. stopSelf() return } try { val notification = NotificationCompat.Builder(this, "CHANNEL_ID") // Create the notification to display while the service is running .build() ServiceCompat.startForeground( /* service = */ this, /* id = */ 100, // Cannot be 0 /* notification = */ notification, /* foregroundServiceType = */ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA } else { 0 }, ) } catch (e: Exception) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && e is ForegroundServiceStartNotAllowedException) { // App not in a valid state to start foreground service // (e.g. started from bg) } // ... } } }
Java
public class MyCameraService extends Service { private void startForeground() { // Before starting the service as foreground check that the app has the // appropriate runtime permissions. In this case, verify that the user // has granted the CAMERA permission. int cameraPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA); if (cameraPermission == PackageManager.PERMISSION_DENIED) { // Without camera permissions the service cannot run in the // foreground. Consider informing user or updating your app UI if // visible. stopSelf(); return; } try { Notification notification = new NotificationCompat.Builder(this, "CHANNEL_ID") // Create the notification to display while the service // is running .build(); int type = 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { type = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA; } ServiceCompat.startForeground( /* service = */ this, /* id = */ 100, // Cannot be 0 /* notification = */ notification, /* foregroundServiceType = */ type ); } catch (Exception e) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && e instanceof ForegroundServiceStartNotAllowedException ) { // App not in a valid state to start foreground service // (e.g started from bg) } // ... } } //... }
Các điểm chính về mã
- Ứng dụng đã khai báo trong tệp kê khai rằng ứng dụng cần có quyền
CAMERA
. Tuy nhiên, ứng dụng cũng phải kiểm tra trong thời gian chạy để đảm bảo người dùng đã cấp quyền đó. Nếu thực sự không có các quyền chính xác, ứng dụng phải cho người dùng biết về vấn đề này. - Các loại dịch vụ trên nền trước khác nhau đã được ra mắt cùng với các phiên bản khác nhau của nền tảng Android. Mã này kiểm tra phiên bản Android mà ứng dụng đang chạy và yêu cầu các quyền thích hợp.
- Mã này kiểm tra
ForegroundServiceStartNotAllowedException
trong trường hợp ứng dụng đang cố gắng khởi động một dịch vụ trên nền trước trong tình huống không được phép (ví dụ: nếu ứng dụng đang cố gắng chuyển dịch vụ lên nền trước trong khi ứng dụng đang ở chế độ nền).