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ị một thanh trạng thái để giúp người dùng biết rằng ứng dụng đang thực hiện một tác vụ ở nền trước và 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 nhận được sự cho phép từ người dùng. Thông báo có thể hiển thị khoảng cách mà người dùng đã đi trong buổi tập thể dục hiện tại.

Chỉ 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 thấy ngay cả khi họ không tương tác trực tiếp ứng dụng. Nếu hành động có tầm 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, tạo nền nhiệm vụ.

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, và cách bắt đầu một dịch vụ trên nền trước cũng như xoá dịch vụ đó khỏi nền. Điều 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, và các hạn chế 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 trong 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), người dùng có thể đóng thông báo được liên kết với dịch vụ trên nền trước theo mặc định. Để làm được điều này, người dùng sẽ vuốt trên thông báo. Thông thường, thông báo không bị loại bỏ trừ phi dịch vụ trên nền trước bị dừng hoặc xoá từ 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 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, ngay cả trên 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 các 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 chúng 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 có <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 có chức năng 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 dịch vụ của bạn có nhiều loại, hãy phân tách các loại đó bằng ký tự | toán tử. Ví dụ: một dịch vụ sử dụng camera và micrô sẽ khai báo lệnh như sau:

android:foregroundServiceType="camera|microphone"

Yêu cầu quyền sử dụng dịch vụ trên nền trướ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ư minh hoạ trong đoạn mã sau. Đây là điều bình thường quyền truy cập, vì vậy, hệ thống tự động cấp quyền truy cập đó 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ẽ đang làm. Mỗi loại dịch vụ trên nền trước có loại quyền tương ứng. Ví dụ: nếu ứng dụng khởi chạy một dịch vụ trên nền trước có sử dụng máy ảnh, bạn phải yêu cầu cả hai FOREGROUND_SERVICEFOREGROUND_SERVICE_CAMERA quyền truy cập. Đây đều là những quyền thông thường nên hệ thống sẽ cấp cho họ 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 những đ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 loại location, hệ thống sẽ kiểm tra để đảm bảo ứng dụng của bạn đã có ACCESS_COARSE_LOCATION hoặc Quyền 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 đã đá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. Dịch vụ trên nền trước tài liệu về loại 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 bắt đầu 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);

Trong dịch vụ, thường là trong onStartCommand(), bạn có thể yêu cầu mà dịch vụ của bạn 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ẽ thực hiện các bước sau: thông số:

Những 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 sẽ cần khai báo cả locationmediaPlayback trong tệp kê khai. Nếu một người dùng bắt đầu chạy và chỉ muốn theo dõi vị trí của họ, ứng dụng của bạn sẽ gọi startForeground() rồi 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ả kiểu 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 nhận một giá trị boolean cho biết liệu có nên xoá thanh trạng thái hay không thông báo. 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
    nút này, một hộp thoại sẽ xuất hiện, liệt kê tên của các ứng dụng. Chiến lược phát hành đĩa đơn
    Nút Dừng ở bên phải mỗi ứ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ó các dịch vụ đang chạy trên nền trước, bất kể ứng dụng đó phiên bản SDK mục tiêu. Thành phần tương tác này, được gọi là Task Manager (Trình quản lý tác vụ), hiển thị một 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 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 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 vào nút Stop (Dừng) bên cạnh ứng dụng của bạn trong Task Manager, thì các thao tác sau sẽ diễn 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 tra xem ứng dụng của bạn có hoạt động như dự kiến trong và sau khi người dùng ngừ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 nhiều cấp độ miễn trừ cho một số loại ứng dụng, mà các phần sau đây mô tả.

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 một 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ụ

Những ứng dụng sau có thể chạy một dịch vụ trên nền trước và 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 những loại ứng dụng sau đây chạy một dịch vụ trên nền trước, những ứ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à bạn có thể dùng dịch vụ trên nền trước. Nếu có được thiết kế cho một mục đích nhất định, nên hầu như lúc nào bạn cũng nên sử dụng API này thay vì dùng một nền trước . Các API được thiết kế theo mục đích thường cung cấp thêm các trường hợp sử dụng cụ thể những khả nă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 để ứ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ề danh sách các loại dịch vụ trên nền trước các phương án thay thế phù hợp 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

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

Ngoài ra, nếu một ứng dụng muốn khởi chạy một dịch vụ trên nền trước cần 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í quyền), thì dịch vụ không thể tạo dịch vụ trong khi ứng dụng đang chạy trong nền. ngay cả khi ứng dụng thuộc một trong các trường hợp miễn trừ khỏi chế độ khởi động ở chế độ nền hạn chế. Lý do cho điều này được giải thích trong phần Hạn chế về bắt đầu các dịch vụ trên nền trước cần dùng trong khi sử dụng quyền truy cập.

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 của bạn 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, bạn cần lưu ý một số tình huống đặc biệt nếu bạn đang bắt đầu một dịch vụ trên nền trước cần 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 kiểm tra khi bạn tạo dịch vụ trên nền trước để đảm bảo ứng dụng 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 loại dịch vụ trên nền trước micrô, chức năng điều hành xác minh rằng ứng dụng của bạn hiện có RECORD_AUDIO quyền. 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 ứng dụng của bạn có khi đang sử dụng, ứ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 dịch vụ trên nền trước thuộc loại camera, vị trí hoặc micrô, hệ thống sẽ nhận 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à ứng dụng sẽ gửi ra SecurityException.

Tương tự, nếu ứng dụng chạy ở chế độ nền và tạo ra một dịch vụ sức khoẻ cần quyền BODY_SENSORS_BACKGROUND, ứng dụng hiện không có quyền đó và hệ thống sẽ gửi ra 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 nhau, như ACTIVITY_RECOGNITION.) Gọi điện PermissionChecker.checkSelfPermission() không ngăn chặn được sự cố này. Nếu ứng dụng có quyền đang sử dụng và phương thức này sẽ gọi checkSelfPermission() để kiểm tra xem nó có quyền đó hay không. sẽ trả về PERMISSION_GRANTED ngay cả khi ứng dụng đang chạy trong nền. Khi phương thức trả về PERMISSION_GRANTED, tức là "ứng dụng của bạn có quyền này 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ủa bạn cần có quyền trong khi sử dụng, bạn phải gọi Context.startForegroundService() hoặc Context.bindService() trong khi ứng dụng của bạn có hoạt động rõ ràng, trừ phi dịch vụ đó thuộc một trong các trường hợp miễn trừ được 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 dịch vụ trên nền trước được khởi động trong khi ứng dụng chạy ở chế độ nền, ứng dụng vẫn có thể truy cập vào thông tin vị trí máy ảnh và micrô trong khi ứng dụng chạy ở nền trước ("trong khi sử dụng").

Trong cùng các trường hợp này, nếu dịch vụ khai báo loại dịch vụ trên nền trướclocation và do một ứng dụng bắt đầu có ACCESS_BACKGROUND_LOCATION thì dịch vụ này luôn có thể truy cập vào thông tin vị trí, ngay cả khi ứng dụng chạy trong 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 ứng dụng các tiện ích.
  • 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 PendingIntent được gửi từ ứng dụng khác nhau.
  • Dịch vụ bắt đầu bởi một ứng dụng là chính sách thiết bị bộ điều khiển 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ằng một ứng dụng có Quyền đặc quyền của 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 giới hạn quyền truy cập vào vị trí, micrô và máy ảnh, thông báo sau xuất hiện trong Logcat:

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