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

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