เปิดบริการที่ทำงานอยู่เบื้องหน้า

การเริ่มบริการที่ทำงานอยู่เบื้องหน้าจากแอปมี 2 ขั้นตอน ขั้นแรกคุณต้องเริ่มบริการโดยเรียกใช้ context.startForegroundService() จากนั้นให้บริการเรียก ServiceCompat.startForeground() เพื่อโปรโมตตัวเองเป็นบริการที่ทำงานอยู่เบื้องหน้า

สิ่งที่ต้องมีก่อน

แอปจะเปิดบริการที่ทำงานอยู่เบื้องหน้าได้เมื่อใดนั้นขึ้นอยู่กับระดับ API ที่แอปกำหนดเป็นเป้าหมาย

  • แอปที่กำหนดเป้าหมายเป็น Android 12 (API ระดับ 31) ขึ้นไปไม่ได้รับอนุญาตให้เริ่มบริการที่ทำงานอยู่เบื้องหน้าขณะที่แอปทำงานอยู่เบื้องหลัง โดยมีข้อยกเว้นบางประการ ดูข้อมูลเพิ่มเติมและข้อมูลเกี่ยวกับข้อยกเว้นของกฎนี้ได้ที่ข้อจำกัดในการเริ่มบริการที่ทำงานอยู่เบื้องหน้าจากเบื้องหลัง

  • แอปที่กำหนดเป้าหมายเป็น Android 14 (API ระดับ 34) ขึ้นไปต้องขอสิทธิ์ที่เหมาะสมสำหรับประเภทบริการที่ทำงานอยู่เบื้องหน้า เมื่อแอปพยายามที่จะส่งเสริมบริการให้แสดงอยู่เบื้องหน้า ระบบจะตรวจสอบสิทธิ์ที่เหมาะสมและแสดงข้อผิดพลาด SecurityException หากแอปไม่มีสิทธิ์ดังกล่าว เช่น หากคุณพยายามเปิดบริการที่ทำงานอยู่เบื้องหน้าประเภท location ระบบจะตรวจสอบว่าแอปของคุณมีสิทธิ์ ACCESS_COARSE_LOCATION หรือ ACCESS_FINE_LOCATION อยู่แล้ว เอกสารประกอบเกี่ยวกับประเภทบริการที่ทำงานอยู่เบื้องหน้าจะแสดงข้อกําหนดเบื้องต้นที่จําเป็นสําหรับบริการที่ทำงานอยู่เบื้องหน้าแต่ละประเภท

เปิดตัวบริการ

หากต้องการเปิดบริการที่ทำงานอยู่เบื้องหน้า คุณต้องเปิดบริการดังกล่าวเป็นบริการทั่วไป (ไม่ใช่ที่ทำงานอยู่เบื้องหน้า) ก่อน โดยทำดังนี้

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

ประเด็นสำคัญเกี่ยวกับรหัส

  • ข้อมูลโค้ดจะเปิดบริการ อย่างไรก็ตาม บริการดังกล่าวยังไม่ได้ทำงานอยู่ในเบื้องหน้า ในบริการเอง คุณต้องเรียกใช้ ServiceCompat.startForeground() เพื่อโปรโมตบริการเป็นบริการที่ทำงานอยู่เบื้องหน้า

โปรโมตบริการให้แสดงอยู่เบื้องหน้า

เมื่อบริการทํางานอยู่ คุณต้องเรียกใช้ ServiceCompat.startForeground() เพื่อขอให้บริการทํางานอยู่เบื้องหน้า โดยปกติแล้วคุณจะเรียกใช้เมธอดนี้ในเมธอด onStartCommand() ของบริการ

ServiceCompat.startForeground() จะใช้พารามิเตอร์ต่อไปนี้

ประเภทบริการที่ทำงานอยู่เบื้องหน้าที่คุณส่งไปยัง startForeground() ประเภทที่ประกาศในไฟล์ Manifest โดยขึ้นอยู่กับ Use Case ที่เฉพาะเจาะจง จากนั้นหากต้องการเพิ่มบริการประเภทอื่นๆ คุณสามารถโทรไปที่ startForeground() อีกครั้ง

ตัวอย่างเช่น สมมติว่าแอปฟิตเนสเรียกใช้บริการติดตามการวิ่งที่ต้องใช้ข้อมูล location เสมอ แต่อาจหรือไม่อาจต้องเล่นสื่อ คุณจะต้องประกาศทั้ง location และ mediaPlayback ในไฟล์ Manifest หากผู้ใช้เริ่มการวิ่งและต้องการติดตามตำแหน่งเท่านั้น แอปของคุณควรเรียกใช้ startForeground() และส่งเฉพาะสิทธิ์ ACCESS_FINE_LOCATION จากนั้นหากผู้ใช้ต้องการเริ่มเล่นเสียง ให้เรียก startForeground() อีกครั้งและส่งค่าผสมแบบบิตของบริการที่ทำงานอยู่เบื้องหน้าทุกประเภท (ในกรณีนี้คือ ACCESS_FINE_LOCATION|FOREGROUND_SERVICE_MEDIA_PLAYBACK)

ตัวอย่างต่อไปนี้แสดงโค้ดที่บริการกล้องจะใช้เพื่อโปรโมตตัวเองเป็นบริการที่ทำงานอยู่เบื้องหน้า

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)
            }
            // ...
        }
    }

    //...
}

ประเด็นสำคัญเกี่ยวกับรหัส

  • แอปได้ประกาศในไฟล์ Manifest แล้วว่าต้องการสิทธิ์ CAMERA อย่างไรก็ตาม แอปต้องตรวจสอบขณะรันไทม์ด้วยเพื่อให้แน่ใจว่าผู้ใช้ให้สิทธิ์นั้นแล้ว หากแอปไม่มีสิทธิ์ที่ถูกต้อง แอปควรแจ้งให้ผู้ใช้ทราบเกี่ยวกับปัญหา
  • บริการที่ทำงานอยู่เบื้องหน้าประเภทต่างๆ เปิดตัวพร้อมกับแพลตฟอร์ม Android เวอร์ชันต่างๆ โค้ดนี้จะตรวจสอบเวอร์ชัน Android ที่ใช้งานอยู่และขอสิทธิ์ที่เหมาะสม
  • โค้ดจะตรวจสอบ ForegroundServiceStartNotAllowedException ในกรณีที่แอปพยายามเริ่มบริการที่ทำงานอยู่เบื้องหน้าในสถานการณ์ที่ไม่อนุญาต (เช่น หากพยายามเลื่อนบริการไปไว้ที่เบื้องหน้าขณะที่แอปอยู่ในเบื้องหลัง)