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 sẽ 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à một số ví dụ về ứng dụng sử 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 đang 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 đó cho phép. Thông báo có thể cho biết quãng đường mà người dùng đã đi 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 nhiệm vụ mà người dùng có thể nhận ra, ngay cả khi họ không tương tác trực tiếp với ứng dụng đó. Nếu thao tác có tầm quan trọng thấp đến mức 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 một nhiệm vụ trong nền.

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

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

Kể từ Android 13 (API cấp 33), người dùng có thể đóng thông báo liên kết với một dịch vụ trên nền trước theo mặc định. Để làm như vậy, người dùng 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ị đóng 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 đóng được thông báo, hãy truyề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ị thông báo ngay lập tức

Nếu một 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, ngay 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, 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 sẽ 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, 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 nhiều loại áp dụng cho dịch vụ của bạn, hãy phân tách các loại đó bằng toán tử |. Ví dụ: một dịch vụ sử dụng máy ảnh và micrô sẽ khai báo 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 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ư trong đoạn mã sau. Đây là một 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, ứ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 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, bạn phải yêu cầu cả quyền FOREGROUND_SERVICEFOREGROUND_SERVICE_CAMERA. Đây đều là các quyền thông thường, vì vậy hệ thống sẽ tự động cấp các quyền này nếu chúng đượ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 của 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ố gắng chạy một dịch vụ trên nền trước thuộc loại 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 rằng bạn đã đáp ứng các điều kiện tiên quyết bắt buộc trước khi bắt đầu một 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 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 tự bắt đầu 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ụ chạy trên 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ẽ lấy 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 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ả 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ỉ 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).

Dưới đây là ví dụ về cách khởi chạy dịch vụ trên nền trước của máy ảnh:

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 nhận một 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ụ này vẫn tiếp tục chạy.

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

Xử lý việc dừng ứng dụng do người dùng thực hiện khi ứng dụng đang chạy dịch vụ trên nền trước

Ở 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 từng ứng dụng
Hình 1. Quy trình làm việc 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ó 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ì. Tính năng này, được gọi là Trình quản lý tác vụ, cho thấy danh sách các ứng dụng hiện đang chạy một dịch vụ trên nền trước.

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

Khi người dùng nhấn 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 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, 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.
  • Dừng mọi hoạt động phát nội dung nghe nhìn.
  • 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 danh sách đã cài đặt.
  • Công việc đã lên lịch sẽ thực thi vào thời gian đã lên lịch.
  • Chuông báo sẽ kêu vào thời gian hoặc khoảng thời gian đã lên lịch.

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

adb shell cmd activity stop-app PACKAGE_NAME

Miễn trừ

Hệ thống cung cấp một số cấp độ miễn trừ cho một số loại ứng dụng nhất định, như mô tả trong các phần sau.

Các trường hợp ngoại lệ được áp dụng cho mỗi ứng dụng, chứ không phải cho mỗi quy trình. Nếu hệ thống miễn 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.

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

Các ứng dụng sau đây 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 Task Manager (Trình quản lý tác vụ):

Các trường hợp ngoại lệ mà người dùng không thể dừng

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

Sử dụng các API được xây dựng theo mục đích thay vì dịch vụ trên nền trước

Đối với nhiều trường hợp sử dụng, bạn có thể sử dụng các API nền tảng hoặc Jetpack để thực hiện công việc mà bạn có thể sử dụng dịch vụ trên nền trước. Nếu có một API được xây dựng cho mục đích phù hợp, bạn gần như luôn nên sử dụng API đó thay vì sử dụng dịch vụ trên nền trước. Các API được xây dựng theo mục đích thường cung cấp các chức năng bổ sung dành riêng cho trường hợp sử dụng mà bạn 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 giải pháp thay thế phù hợp để sử dụng thay vì dịch vụ trên nền trướ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 ứng dụng đang chạy ở chế độ nền, ngoại trừ một vài 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 và dịch vụ trên nền trước không đáp ứng một trong các trường hợp ngoại lệ, thì hệ thống sẽ gửi một ForegroundServiceStartNotAllowedException.

Ngoài ra, nếu muốn chạy một dịch vụ trên nền trước cần quyền khi đang sử dụng (ví dụ: quyền truy cập vào cảm biến cơ thể, máy ảnh, micrô hoặc thông tin vị trí), thì ứng dụng không thể tạo dịch vụ đó khi đ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ừ khỏi các quy định hạn chế về việc bắt đầu ở chế độ nền. Lý do của việc này được giải thích trong phần Hạn chế về việc khởi động các dịch vụ trên nền trước cần có quyền khi đang sử dụng.

Miễn các hạn chế khi bắt đầu chạy ở chế độ nền

Trong các 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:

Các hạn chế về việc bắt đầu các dịch vụ trên nền trước cần có quyền khi đang sử dụng

Trên Android 14 (API cấp 34) trở lên, bạn cần lưu ý một số trường hợp đặc biệt nếu đang bắt đầu một dịch vụ trên nền trước cần quyền khi đang 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 dịch vụ trên nền trước để đảm bảo ứng dụng của bạn có tất 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, điều này có thể gây ra vấn đề. Nếu có quyền khi đang sử dụng, ứng dụng của bạn chỉ có quyền đó khi đang ở chế độ nền trước. Điều này có nghĩa là nếu ứng dụng của bạn đang 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 một SecurityException.

Tương tự, nếu ứng dụng của bạn đang chạy ở chế độ nền và tạo một dịch vụ sức khoẻ cần quyền BODY_SENSORS, thì ứng dụng hiện không có quyền đó và hệ thống sẽ gửi một ngoại lệ. (Điều này không áp dụng nếu đó là dịch vụ y tế cần các quyền khác, như ACTIVITY_RECOGNITION.) Việc gọi PermissionChecker.checkSelfPermission() sẽ không ngăn chặn được vấn đề này. Nếu ứng dụng của bạn có quyền khi đang sử dụng và 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, tức là "ứng dụng của bạn có quyền này trong khi ứng dụng đang được sử dụng".

Vì lý do này, nếu dịch vụ trên nền trước của bạn cần quyền khi đang sử dụng, bạn phải gọi Context.startForegroundService() hoặc Context.bindService() trong khi ứng dụng có một 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 khi đang sử dụng

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

Trong những trường hợp tương tự, nếu dịch vụ khai báo loại dịch vụ trên nền trướclocation và được khởi động bởi một ứng dụng có quyền ACCESS_BACKGROUND_LOCATION, thì dịch vụ này có thể truy cập 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 bao gồm các trường hợp này:

  • Một thành phần hệ thống sẽ khởi động dịch vụ.
  • Dịch vụ bắt đầu bằng cách tương tác với 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 được gửi từ một ứng dụng khác, hiển thị.
  • Dịch vụ này bắt đầu bằng một ứng dụng là trình điều khiển 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ụ này bắt đầu bằng một ứng dụng có quyền đặc quyền START_ACTIVITIES_FROM_BACKGROUND.
Xác định những dịch vụ bị ảnh hưởng trong ứng dụng

Khi kiểm thử ứng dụng, hãy bắt đầu các dịch vụ trên nền trước của ứng dụng. Nếu một dịch vụ đã bắt đầu hạn chế quyền truy cập vào vị trí, micrô và máy ảnh, 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