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

การเปิดใช้บริการที่ทำงานอยู่เบื้องหน้าจากแอปมี 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 ในกรณีที่พยายามเริ่มบริการที่ทำงานอยู่เบื้องหน้าในสถานการณ์ที่ไม่ได้รับอนุญาต (เช่น หากพยายามโปรโมตบริการไปยังเบื้องหน้าขณะที่แอปอยู่ในเบื้องหลัง)