Có hai bước để chạy dịch vụ trên nền trước từ ứng dụng. 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 yêu cầu dịch vụ gọi ServiceCompat.startForeground()
để tự quảng bá 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, sẽ có một số hạn chế về thời điểm ứng dụng có thể chạy 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 khởi động dịch vụ trên nền trước trong khi ứng dụng đang chạy ở chế độ nền, ngoại trừ một vài trường hợp ngoại lệ 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 phần Hạn chế khi khởi động dịch vụ trên nền trước từ nền.
Các ứ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 phù hợp cho loại dịch vụ trên nền trước. Khi ứng dụng cố gắng đẩy 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.
Chạy một dịch vụ
Để chạy một dịch vụ trên nền trước, trước tiên, bạn phải chạy dịch vụ đó dưới dạng một dịch vụ thông thường (không phải 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 sẽ 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. Bên trong chính dịch vụ, bạn cần gọi
ServiceCompat.startForeground()
để quảng bá dịch vụ đó thành một dịch vụ trên nền trước.
Đưa 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()
nhận các thông số sau:
- Dịch vụ.
- Một số nguyên dương giúp xác định duy nhấ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 một dịch vụ theo dõi 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 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à dịch vụ máy ảnh sẽ sử dụng để tự quảng bá lên 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 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 ứng dụng không thực sự có quyền chính xác, thì ứng dụng đó phải cho người dùng biết về vấn đề. - Các loại dịch vụ trên nền trước khác nhau được giới thiệu với các phiên bản nền tảng Android khác nhau. Mã này kiểm tra phiên bản Android đang chạy và yêu cầu các quyền thích hợp.
- Mã này sẽ kiểm tra
ForegroundServiceStartNotAllowedException
trong trường hợp mã đó đang cố gắng khởi động một dịch vụ trên nền trước trong một tình huống không được phép (ví dụ: nếu mã đó đang cố gắng quảng bá dịch vụ lên nền trước trong khi ứng dụng đang chạy ở chế độ nền).