Service เป็นหนึ่งใน App Component พื้นฐานของระบบปฏิบัติการณ์แอนดรอยด์ที่ถูกออกแบบมาสำหรับการทำงานที่ต้องการให้อยู่ในเบื้องหลัง (Operation in Background)

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

การทำงานเบื้องหลังของ Service จะเป็นคนละอย่างกับ Asynchronous Task (Thread หรือ Kotlin Coroutines) อย่าสับสนล่ะ

บทความในซีรีย์เดียวกัน

Service แต่ละประเภท

โดย Service จะถูกแบ่งออกเป็น 3 ประเภทใหญ่ ๆ ด้วยกันดังนี้

  • Background Service
  • Foreground Service
  • Bound Service

Background Service

เป็นการทำงานในเบื้องหลังที่ไม่จำเป็นต้องแจ้งข้อมูลใด ๆ ให้ผู้ใช้ทราบ จึงเหมาะกับการทำงานที่ไม่เกี่ยวข้องกับ UI หรือไม่จำเป็นต้องโต้ตอบกับผู้ใช้

Foreground Service

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

Bound Service

เป็นการผูก Service เข้ากับ App Component ที่เป็นคนสร้าง Bound Service ตัวนั้นขึ้นมา โดยที่ Bound Service จะมี Lifetime เทียบเท่ากับ App Component ตัวนั้น และรับส่งข้อมูลระหว่างกันผ่าน IPC (Interprocess Communication) ได้ด้วย

ควรเลือกใช้งาน Service ให้เหมาะสมกับงาน

การใช้ Service แต่ละแบบจะมีข้อจำกัดที่แตกต่างกัน (และถ้าไม่จำเป็นจริง ๆ ก็ไม่ควรใช้ตั้งแต่แรก) เพื่อให้นักพัฒนาเลือกใช้ Service แต่ละแบบที่ตรงกับความต้องการได้ ยกตัวอย่างเช่น

  • ใช้ WebSocket และต้องการให้รับส่งข้อมูลเฉพาะตอนที่เปิดใช้งานแอปเท่านั้น
    Bound Service
  • Background Sync เพื่อดาวน์โหลดข้อมูลจาก Web Service มาเก็บไว้ในแอปทุกเช้า
    Background Service
  • Upload Video ที่ต้องใช้ระยะเวลานาน โดยที่ผู้ใช้สามารถสลับไปทำอย่างอื่นในแอปหรือสลับไปแอปอื่นได้
    Foreground Service
  • ทำ Location Tracking เพื่อจับตำแหน่งของอุปกรณ์ตลอดเวลาที่ใช้งานแอป โดยไม่จำเป็นต้องเปิดแอปตลอดเวลา
    Foreground Service
นักพัฒนาอาจจะเลือกใช้ Service ที่แตกต่างจากตัวอย่างข้างต้นได้ เพราะเป็นสิ่งที่ขึ้นอยู่กับ Requirement หรือ Use Case ของแต่ละแอป

สิ่งที่ควรรู้เมื่อเลือกใช้ Service ในแต่ละประเภท

เนื่องจาก Service แต่ละประเภทมีรูปแบบในการทำงานที่ต่างกัน ดังนั้นนักพัฒนาจะต้องเข้าใจข้อจำกัดหรือรูปแบบการเท่างานเหล่านั้นด้วย เพื่อออกแบบการทำงานของแอปให้เหมาะสมกับ Service ที่เลือกใช้งาน

Background Service

เนื่องจากเป็น Service ที่ไม่ได้ขึ้นอยู่กับ UI เหมือนกับ Service ตัวอื่น จึงทำให้ Background Service เป็น Service ที่ถูกเพ่งเล็งโดยระบบแอนดรอยด์มากที่สุด เพราะเป็นหนึ่งในสาเหตุหลักที่ทำให้อุปกรณ์แอนดรอยด์ช้ากว่าที่ควรจะเป็น

เพราะผู้ใช้ไม่รู้ว่าในเครื่องมี Background Service ของแอปไหนที่แอบทำงานอยู่บ้าง

จนกระทั่งใน Android 8.0 Oreo (API Level 26) เพิ่มสิ่งที่เรียกว่า Background Service Limitations เข้ามาในระบบแอนดรอยด์เพื่อทำให้ Background Service ทำงานได้แค่เพียงระยะเวลาหนึ่งหลังจากแอปเปลี่ยนสถานะเป็น Background ก่อนที่จะถูกระบบปิดทิ้งเพื่อไม่ให้กินทรัพยากรเครื่องเกินจำเป็น

นับตั้งแต่นั้นเป็นต้นมา Background Service จึงเหมาะกับการทำงานที่เป็น Short-lived เป็นหลัก ส่วนการทำงานแบบ Long-running จะต้องเปลี่ยนไปใช้ Foreground Service หรือเปลี่ยนไปใช้วิธีอื่นแทน (เช่น AndroidX WorkManager)

Foreground Service

เนื่องจาก Foreground Service จะต้องมี Notification เพื่อแจ้งให้ผู้ใช้ทราบอยู่ตลอดเวลาจึงเหมาะกับการทำงานในลักษณะของ Long-running ที่ผู้ใช้ไม่จำเป็นต้องเปิดแอปตลอดเวลา และมีเงื่อนไขว่าแอปจะสร้าง Foreground Service ได้ก็ต่อเมื่ออยู่ในสถานะ Foreground เท่านั้น

นอกจากนี้ในแอนดรอยด์บางเวอร์ชันก็เพิ่มข้อบังคับและการทำงานอย่างสำหรับ Foreground Service เข้ามาด้วย

  • Android 9 Pie (API Level 28) – ต้องประกาศ Permission สำหรับ Foreground Service ไว้ใน Android Manifest ด้วย
  • Android 10 (API Level 29) – Foreground Service ที่เข้าถึงตำแหน่งของอุปกรณ์ (Location Access) จะต้องระบุประเภทของ Foreground Service เป็น location ไว้ด้วย
  • Android 11 (API Level 30) – Foreground Service ที่ใช้กล้องหรือไมค์จะต้องระบุประเภทของ Foreground Service เป็น camera และ microphone ไว้ด้วย และถ้า Foreground Service อยู่ในสถานะ Background ก็จะเข้าถึงกล้องและไมค์ไม่ได้อีกต่อไป
  • Android 12 (API Level 31) – แอปที่อยู่ในสถานะ Background จะสร้าง Foreground Service ไม่ได้ได้อีกต่อไป (มีเวลาให้อยู่นิดนึงตอนที่แอปเปลี่ยนจากสถานะ Foreground ไปเป็น Background) และสำหรับ Short-lived Foreground Service ในบางกรณีจะมีเวลาให้ประมาณ​ 10 วินาทีก่อนที่ระบบจะแสดง Notification
  • Android 13 (API Level 33) – ผู้ใช้สามารถปัดที่ Notification ของ Foreground Service เพื่อปิด Foreground Service ที่ไม่ได้กำหนดค่าใน Notification เป็น Ongoing ได้
  • Android 14 (API Level 34) – แอปทุกตัวจะต้องระบุประเภทของ Foreground Service ให้กับ Foreground Service ทุกตัวที่มีอยู่ในแอป และจะต้องเพิ่ม Permission สำหรับ Foreground Service แต่ละประเภทด้วย

และนอกจากนี้แอปที่กำหนด Target SDK Level เป็น 34 (Android 14) จะต้องส่งข้อมูลเพิ่มเติมบน Google Play Console เพื่ออธิบายให้กับทีมรีวิวของ Google Play ว่าการทำงานไหนภายในแอปบ้างที่จำเป็นต้องใช้ Foreground Service ด้วย

Bound Service

เนื่องจาก Bound Service ผูกกับ App Component ที่เรียกใช้งาน จึงเหมาะกับการทำงานที่ทำควบคู่กับ App Component ตัวนั้น แต่ในขณะเดียวกันก็ทำให้การใช้งาน Bound Service จะขึ้นอยู่กับการออกแบบโครงสร้างภายในแอปด้วย

ยกตัวอย่างเช่น แอปจำเป็นต้องใช้ WebSocket เพื่อรับข้อมูลจาก Web Service ได้ตลอดเวลาไม่ว่าจะเปิดหน้าไหนอยู่ก็ตาม จึงให้คำสั่งดังกล่าวอยู่ใน Bound Service และผูกไว้กับ Activity โดยจะมีเงื่อนไขว่าแอปควรจะเป็น Single Activity ที่แต่ละหน้าอยู่ภายใต้ Fragment หรือ Jetpack Compose ทั้งหมดเพื่อให้ Activity ตัวนั้น Active อยู่ตลอดเวลา

ควรใช้ AndroidX WorkManager แทนการสร้าง Service ด้วยตัวเอง (ถ้าเป็นไปได้)

สำหรับการทำงานส่วนใหญ่โดยเฉพาะ Short-lived Background Service สามารถใช้ WorkManager แทนได้ ซึ่งเป็นหนึ่งใน Library ของ Android Jetpack ที่มีการใช้ Job Scheduler อยู่เบื้องหลัง

นอกจากนี้ยังรองรับการทำงานแบบ Long-running ในบางรูปแบบได้ด้วย โดยจะสร้างเป็น Foreground Service อยู่เบื้องหลัง

App Architecture: Data Layer - Schedule Task with WorkManager - Android Developers
Explore this app architecture guide on data layer libraries to learn about types of persistent work, features, and more.

ดังนั้นงานที่จำเป็นต้องใช้ Service ที่ไม่ใช่ Bound Service ก็แนะนำให้พิจารณาจากความสามารถของ WorkManager ก่อน เพราะเป็น Library ที่ใช้งานได้ง่าย ตอบโจทย์กับความต้องการส่วนใหญ่ได้ พัฒนาโดยทีมแอนดรอยด์ที่คอยจัดการกับการทำงานบนแอนดรอยด์แต่ละเวอร์ชัน และมีเครื่องมือที่จะช่วยให้นักพัฒนา Debug การทำงานของ WorkManager ได้ง่ายด้วย

และถ้าความสามารถของ WorkManager ไม่ตอบโจทย์กับการทำงานที่ต้องการ ถึงตอนนั้นก็ค่อยตัดสินใจสร้าง Service ขึ้นมาเองก็ได้

Service ที่อยู่ยงคงกระพัน มีแต่ System App เท่านั้น

นักพัฒนาบางคนอาจจะมองหา Solution ที่ทำให้ Service ไม่ถูกทำลายหรือหยุดทำงานโดยระบบแอนดรอยด์ ซึ่งความสามารถดังกล่าวมีให้เฉพาะแอปที่มี Privilege ในระดับ System App เท่านั้น ดังนั้นถ้าเป็นแอปทั่วไปที่ติดตั้งผ่าน Google Play จะไม่มีทางทำแบบนั้นได้เลย

ดังนั้นถ้าไม่ใช่ System App ก็ควรหยุดหา Solution แล้วเปลี่ยนการทำงานภายในแอปให้เหมาะสมแทนดีกว่า

Service เก่งแค่ไหนก็แพ้ Force Stop

ถึงแม้ว่าการปิดแอปจาก Recents screen จะไม่ทำให้ Service ของแอปนั้นถูกทำลาย แต่ถ้าผู้ใช้กด Force Stop จากหน้า App Info ก็จะเป็นการปิดแอปและ Service ที่ทำงานอยู่ทั้งหมด ซึ่งมีผลกับ System App ด้วยเช่นกัน

Service กับ Location Access

นับตั้งแต่ Android 12 (API Level 31) เป็นต้นไป แอปที่ต้องการใช้งาน Location Access ใน Service จะต้องขอ Permission ที่ชื่อว่า ACCESS_BACKGROUND_LOCATION ในตอน Runtime ด้วย เพื่อให้ใช้งานในขณะที่แอปอยู่ในสถานะ Background ได้ จากเดิมที่ขอแค่ ACCESS_FINE_LOCATION หรือ ACCESS_COAST_LOCATION ก็พอ

และจะต้องส่งรายละเอียดสำหรับการใช้ Permission ดังกล่าวให้กับทีมรีวิวของ Google Play ด้วย (แบบเดียวกับ Foreground Service)

สรุป

ถึงแม้ว่าส่วนใหญ่เราจะเรียกการทำงานของ Service บนแอนดรอยด์เหล่านี้แบบรวม ๆ ว่า Background Service แต่ทว่าระบบแอนดรอยด์ก็แบ่งรูปแบบในการทำงานของ Service ออกเป็นหลายรูปแบบ และมีชื่อเรียกที่แตกต่างกันออกไป ไม่ได้มีแค่คำว่า Background Service เท่านั้น

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

แหล่งข้อมูลอ้างอิง