Dịch vụ trên nền trước

Dịch vụ trên nền trước thực hiện các hoạt động mà người dùng có thể nhận thấy.

Dịch vụ trên nền trước hiển thị thông báo trên thanh trạng thái để người dùng biết rằng ứng dụng của bạn đang thực hiện một nhiệm vụ ở nền trước và đang sử dụng tài nguyên hệ thống.

Sau đây là ví dụ về ứng dụng dùng dịch vụ trên nền trước:

  • Một ứng dụng trình phát nhạc có chức năng phát nhạc trong dịch vụ trên nền trước. Thông báo có thể hiển thị bài hát hiện tại đang được phát.
  • Một ứng dụng thể dục ghi lại quãng đường chạy của một người dùng trong dịch vụ trên nền trước sau khi được người dùng đó cấp quyền. Thông báo có thể hiển thị quãng đường mà người dùng đã đi được trong phiên tập thể dục hiện tại.

Chỉ sử dụng dịch vụ trên nền trước khi ứng dụng của bạn cần thực hiện một tác vụ mà người dùng có thể nhận thấy, ngay cả khi họ không tương tác trực tiếp với ứng dụng. Nếu hành động đó có mức độ quan trọng đủ thấp mà bạn muốn sử dụng thông báo có mức độ ưu tiên tối thiểu, hãy tạo tác vụ trong nền.

Tài liệu này mô tả quyền cần thiết để sử dụng các dịch vụ trên nền trước, cũng như cách bắt đầu một dịch vụ trên nền trước và xoá dịch vụ đó khỏi chế độ nền. Hướng dẫn này cũng mô tả cách liên kết một số trường hợp sử dụng với các loại dịch vụ trên nền trước, cũng như các hạn chế truy cập có hiệu lực khi bạn bắt đầu dịch vụ trên nền trước từ một ứng dụng đang chạy ở chế độ nền.

Người dùng có thể đóng thông báo theo mặc định

Kể từ Android 13 (API cấp 33), theo mặc định, người dùng có thể đóng thông báo liên kết với dịch vụ trên nền trước. Để làm vậy, người dùng sẽ thực hiện cử chỉ vuốt trên thông báo. Thông thường, thông báo sẽ không bị loại bỏ trừ phi dịch vụ trên nền trước bị dừng hoặc bị xoá khỏi nền trước.

Nếu bạn muốn người dùng không thể đóng thông báo, hãy chuyển true vào phương thức setOngoing() khi bạn tạo thông báo bằng Notification.Builder.

Những dịch vụ hiện thông báo ngay lập tức

Nếu dịch vụ trên nền trước có ít nhất một trong các đặc điểm sau, thì hệ thống sẽ hiển thị thông báo liên quan ngay sau khi dịch vụ bắt đầu, kể cả trên các thiết bị chạy Android 12 trở lên:

Trên Android 13 (API cấp 33) trở lên, nếu người dùng từ chối quyền gửi thông báo, thì họ vẫn thấy thông báo liên quan đến các dịch vụ trên nền trước trong Trình quản lý tác vụ nhưng không thấy các thông báo đó trong ngăn thông báo.

Khai báo dịch vụ trên nền trước trong tệp kê khai

Trong tệp kê khai của ứng dụng, hãy khai báo từng dịch vụ trên nền trước của ứng dụng bằng phần tử <service>. Đối với mỗi dịch vụ, hãy sử dụng thuộc tính android:foregroundServiceType để khai báo loại công việc mà dịch vụ đó thực hiện.

Ví dụ: nếu ứng dụng của bạn tạo một dịch vụ trên nền trước để phát nhạc, thì bạn có thể khai báo dịch vụ đó như sau:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
  <application ...>

    <service
        android:name=".MyMediaPlaybackService"
        android:foregroundServiceType="mediaPlayback"
        android:exported="false">
    </service>
  </application>
</manifest>

Nếu bạn áp dụng nhiều kiểu cho dịch vụ của mình, hãy phân tách các kiểu đó bằng toán tử |. Ví dụ: một dịch vụ sử dụng máy ảnh và micrô sẽ khai báo dịch vụ như sau:

android:foregroundServiceType="camera|microphone"

Yêu cầu quyền sử dụng dịch vụ trên nền trước

Các ứng dụng nhắm đến Android 9 (API cấp 28) trở lên và sử dụng các dịch vụ trên nền trước cần yêu cầu FOREGROUND_SERVICE trong tệp kê khai ứng dụng, như minh hoạ trong đoạn mã sau. Đây là quyền thông thường, vì vậy, hệ thống sẽ tự động cấp quyền này cho ứng dụng yêu cầu.

Ngoài ra, nếu nhắm đến API cấp 34 trở lên, thì ứng dụng phải yêu cầu loại quyền thích hợp cho loại công việc mà dịch vụ trên nền trước sẽ thực hiện. Mỗi loại dịch vụ trên nền trước đều có một loại quyền tương ứng. Ví dụ: nếu một ứng dụng chạy một dịch vụ trên nền trước sử dụng máy ảnh, thì bạn phải yêu cầu cả quyền FOREGROUND_SERVICEFOREGROUND_SERVICE_CAMERA. Đây đều là những quyền thông thường, vì vậy, hệ thống sẽ tự động cấp những quyền này nếu được liệt kê trong tệp kê khai.

<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA"/>

    <application ...>
        ...
    </application>
</manifest>

Điều kiện tiên quyết đối với dịch vụ trên nền trước

Kể từ Android 14 (API cấp 34), khi bạn chạy một dịch vụ trên nền trước, hệ thống sẽ kiểm tra các điều kiện tiên quyết cụ thể dựa trên loại dịch vụ. Ví dụ: nếu bạn cố chạy một dịch vụ trên nền trước thuộc kiểu location, hệ thống sẽ kiểm tra để đảm bảo ứng dụng của bạn đã có quyền ACCESS_COARSE_LOCATION hoặc ACCESS_FINE_LOCATION. Nếu không, hệ thống sẽ gửi SecurityException.

Vì lý do này, bạn phải xác nhận đã đáp ứng các điều kiện tiên quyết bắt buộc trước khi bắt đầu dịch vụ trên nền trước. 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.

Bắt đầu dịch vụ trên nền trước

Trước khi bạn yêu cầu hệ thống chạy một dịch vụ dưới dạng dịch vụ trên nền trước, hãy khởi động chính dịch vụ đó:

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);

Bên trong dịch vụ, thường là trong onStartCommand(), bạn có thể yêu cầu dịch vụ của mình chạy ở nền trước. Để thực hiện việc này, hãy gọi ServiceCompat.startForeground() (có trong androidx-core 1.12 trở lên). Phương thức này sẽ sử dụng các thông số sau:

Các loại này có thể là một tập hợp con của 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ụ trình theo dõi hoạt động chạy 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ả locationmediaPlayback 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ỉ chuyể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 bitwise 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).

Dưới đây là một ví dụ khởi chạy dịch vụ máy ảnh 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)
            }
            // ...
        }
    }

    //...
}

Xoá một dịch vụ khỏi nền trước

Để xoá dịch vụ khỏi nền trước, hãy gọi stopForeground(). Phương thức này sẽ sử dụng một giá trị boolean cho biết liệu có xoá thông báo trên thanh trạng thái hay không. Xin lưu ý rằng dịch vụ sẽ tiếp tục chạy.

Nếu bạn dừng dịch vụ trong khi chạy ở nền trước, thì thông báo của dịch vụ đó sẽ bị xoá.

Xử lý việc dừng các ứng dụng chạy dịch vụ trên nền trước theo yêu cầu của người dùng

Ở cuối ngăn thông báo là một nút cho biết số lượng ứng dụng hiện đang chạy ở chế độ nền. Khi bạn nhấn vào nút này, một hộp thoại sẽ xuất hiện, trong đó liệt kê tên của các ứng dụng. Nút Dừng nằm ở bên phải của mỗi ứng dụng
Hình 1. Quy trình của Trình quản lý tác vụ trên các thiết bị chạy Android 13 trở lên.

Kể từ Android 13 (API cấp 33), người dùng có thể hoàn tất quy trình công việc từ ngăn thông báo để dừng một ứng dụng có các dịch vụ đang chạy trên nền trước, bất kể phiên bản SDK mục tiêu của ứng dụng đó là gì. Thành phần tương tác này (được gọi là Task Manager) cho thấy danh sách các ứng dụng hiện đang chạy dịch vụ trên nền trước.

Danh sách này được gắn nhãn Ứng dụng đang hoạt động. Bên cạnh mỗi ứng dụng là nút Stop (Dừng). Hình 1 minh hoạ quy trình công việc của Trình quản lý tác vụ trên thiết bị chạy Android 13.

Khi người dùng nhấn vào nút Stop (Dừng) bên cạnh ứng dụng của bạn trong Task Manager (Trình quản lý tác vụ), các thao tác sau đây sẽ xảy ra:

  • Hệ thống sẽ xoá ứng dụng của bạn khỏi bộ nhớ. Do đó, toàn bộ ứng dụng của bạn sẽ dừng, chứ không chỉ dịch vụ trên nền trước đang chạy.
  • Hệ thống sẽ xoá ngăn xếp lui hoạt động của ứng dụng.
  • Mọi quá trình phát nội dung nghe nhìn đều sẽ dừng.
  • Thông báo liên kết với dịch vụ trên nền trước sẽ bị xoá.
  • Ứng dụng của bạn vẫn còn trong nhật ký.
  • Các lệnh đã lên lịch sẽ thực thi vào thời gian đã lên lịch.
  • Chuông báo sẽ tắt theo khung thời gian hoặc thời gian đã lên lịch.

Để kiểm thử xem ứng dụng có hoạt động như dự kiến trong khi và sau khi người dùng dừng ứng dụng hay không, hãy chạy lệnh ADB sau đây trong cửa sổ dòng lệnh:

adb shell cmd activity stop-app PACKAGE_NAME

Miễn trừ

Hệ thống đưa ra một số cấp độ miễn trừ cho một số loại ứng dụng cụ thể, theo mô tả trong các phần sau đây.

Các trường hợp miễn trừ được áp dụng theo ứng dụng chứ không phải theo quy trình. Nếu hệ thống miễn trừ một quy trình trong ứng dụng, thì tất cả các quy trình khác trong ứng dụng đó cũng được miễn trừ.

Miễn xuất hiện trong Trình quản lý tác vụ

Các ứng dụng sau có thể chạy dịch vụ trên nền trước và hoàn toàn không xuất hiện trong Trình quản lý tác vụ:

Miễn trừ khỏi việc người dùng có thể dừng hoạt động

Khi các loại ứng dụng sau chạy dịch vụ trên nền trước, các ứng dụng đó sẽ xuất hiện trong Trình quản lý tác vụ, nhưng không có nút Stop (Dừng) bên cạnh tên ứng dụng để người dùng nhấn vào:

Dùng API được thiết kế riêng cho từng mục đích thay vì dịch vụ trên nền trước

Trong nhiều trường hợp sử dụng, bạn có thể dùng API nền tảng hoặc API Jetpack để thực hiện công việc mà thường sẽ sử dụng dịch vụ trên nền trước. Nếu có một API được xây dựng phù hợp với mục đích sử dụng, bạn nên hầu như luôn sử dụng API đó thay vì sử dụng dịch vụ trên nền trước. API được thiết kế có mục đích thường cung cấp thêm các tính năng cụ thể trong trường hợp sử dụng mà bạn lẽ ra phải tự xây dựng. Ví dụ: Bubbles API xử lý logic giao diện người dùng phức tạp cho các ứng dụng nhắn tin cần triển khai các tính năng bong bóng trò chuyện.

Tài liệu về các loại dịch vụ trên nền trước liệt kê các phương án thay thế phù hợp để dùng thay cho dịch vụ trên nền trước.

Các hạn chế khi bắt đầu dịch vụ trên nền trước từ chế độ nền

Các ứng dụng nhắm đến Android 12 trở lên không thể bắt đầu các dịch vụ trên nền trước trong khi đang chạy ở chế độ nền, ngoại trừ một số trường hợp đặc biệt. Nếu một ứng dụng cố gắng bắt đầu một dịch vụ trên nền trước trong khi ứng dụng đó chạy ở chế độ nền, nhưng dịch vụ đó trên nền trước không đáp ứng được một trong các trường hợp đặc biệt, thì hệ thống sẽ gửi ra một ForegroundServiceStartNotAllowedException.

Ngoài ra, nếu một ứng dụng muốn chạy một dịch vụ trên nền trước cần có các quyền trong khi sử dụng (ví dụ: quyền đối với cảm biến cơ thể, máy ảnh, micrô hoặc vị trí), thì ứng dụng đó không thể tạo dịch vụ đó khi ứng dụng đang chạy ở chế độ nền, ngay cả khi ứng dụng thuộc một trong các trường hợp miễn trừ do các hạn chế về việc khởi động ở chế độ nền. Lý do cho điều này được giải thích trong phần Hạn chế khi bắt đầu các dịch vụ trên nền trước cần có quyền trong khi sử dụng.

Miễn khỏi các hạn chế về khởi động ở chế độ nền

Trong những trường hợp sau, ứng dụng của bạn có thể bắt đầu các dịch vụ trên nền trước ngay cả khi ứng dụng đó chạy ở chế độ nền:

Quy định hạn chế về việc khởi động những dịch vụ trên nền trước cần có quyền trong khi sử dụng

Trên Android 14 (API cấp 34) trở lên, có những tình huống đặc biệt cần lưu ý nếu bạn đang khởi động một dịch vụ trên nền trước cần có quyền trong khi sử dụng.

Nếu ứng dụng của bạn nhắm đến Android 14 trở lên, thì hệ điều hành sẽ kiểm tra thời điểm bạn tạo một dịch vụ trên nền trước để đảm bảo ứng dụng có tất cả các quyền thích hợp cho loại dịch vụ đó. Ví dụ: khi bạn tạo một dịch vụ trên nền trước thuộc loại micrô, hệ điều hành sẽ xác minh rằng ứng dụng của bạn hiện có quyền RECORD_AUDIO. Nếu bạn không có quyền đó, hệ thống sẽ gửi ra một SecurityException.

Đối với các quyền khi đang sử dụng, việc này có thể gây ra vấn đề. Nếu có quyền trong khi sử dụng, thì ứng dụng chỉ có quyền đó khi đang ở nền trước. Tức là nếu ứng dụng của bạn chạy ở chế độ nền và cố gắng tạo một dịch vụ trên nền trước thuộc loại máy ảnh, vị trí hoặc micrô, thì hệ thống sẽ thấy rằng ứng dụng của bạn hiện không có các quyền cần thiết và sẽ gửi ra một SecurityException.

Tương tự, nếu ứng dụng của bạn chạy ở chế độ nền và tạo ra một dịch vụ sức khoẻ cần quyền BODY_SENSORS_BACKGROUND, thì ứng dụng hiện không có quyền đó và hệ thống sẽ gửi ra một trường hợp ngoại lệ. (Điều này không áp dụng nếu đó là một dịch vụ y tế cần các quyền khác, chẳng hạn như ACTIVITY_RECOGNITION.) Việc gọi PermissionChecker.checkSelfPermission() không không ngăn được vấn đề này. Nếu ứng dụng của bạn có quyền khi đang sử dụng và ứng dụng này gọi checkSelfPermission() để kiểm tra xem ứng dụng có quyền đó hay không, thì phương thức này sẽ trả về PERMISSION_GRANTED ngay cả khi ứng dụng đang chạy ở chế độ nền. Khi phương thức này trả về PERMISSION_GRANTED, có nghĩa là "ứng dụng của bạn có quyền này trong khi ứng dụng đang được dùng".

Vì lý do này, nếu dịch vụ trên nền trước cần có quyền khi đang sử dụng, thì bạn phải gọi Context.startForegroundService() hoặc Context.bindService() trong khi ứng dụng có hoạt động hiển thị, trừ phi dịch vụ đó thuộc một trong các trường hợp miễn trừ đã xác định.

Miễn khỏi các hạn chế về quyền trong khi sử dụng

Trong một số trường hợp, ngay cả khi khởi động một dịch vụ trên nền trước trong khi ứng dụng chạy ở chế độ nền, thì ứng dụng đó vẫn có thể truy cập vào thông tin vị trí, camera và micrô khi ứng dụng đó chạy ở nền trước ("trong khi sử dụng").

Trong những trường hợp tương tự như vậy, nếu dịch vụ khai báo loại dịch vụ trên nền trướclocation và được bắt đầu bởi một ứng dụng có quyền ACCESS_BACKGROUND_LOCATION, thì dịch vụ này có thể truy cập vào thông tin vị trí mọi lúc, ngay cả khi ứng dụng chạy ở chế độ nền.

Danh sách sau đây chứa những trường hợp sau:

  • Một thành phần hệ thống khởi động dịch vụ.
  • Dịch vụ bắt đầu bằng cách tương tác với các tiện ích ứng dụng.
  • Dịch vụ bắt đầu bằng cách tương tác với một thông báo.
  • Dịch vụ bắt đầu dưới dạng một PendingIntent gửi từ một ứng dụng khác đang hiển thị.
  • Dịch vụ này bắt đầu bằng một ứng dụng là trình kiểm soát chính sách thiết bị chạy ở chế độ chủ sở hữu thiết bị.
  • Dịch vụ này bắt đầu bằng một ứng dụng cung cấp VoiceInteractionService.
  • Dịch vụ bắt đầu bởi một ứng dụng có đặc quyền START_ACTIVITIES_FROM_BACKGROUND.
Xác định những dịch vụ bị ảnh hưởng trong ứng dụng của bạn

Khi kiểm thử ứng dụng, hãy bắt đầu các dịch vụ trên nền trước. Nếu một dịch vụ đã bắt đầu hạn chế quyền truy cập vào vị trí, micrô và camera, thì thông báo sau sẽ xuất hiện trong Logcat:

Foreground service started from background can not have \
location/camera/microphone access: service SERVICE_NAME