Background Service — คือคอมโพเนนต์ของ Android ที่ออกแบบมาสำหรับการดำเนินการที่ยาวนานในเบื้องหลังโดยไม่มีอินเทอร์เฟซผู้ใช้ แตกต่างจาก Activity ตรงที่ Service ยังคงทำงานต่อไปแม้ว่าแอปจะถูกย่อหรือผู้ใช้สลับไปยังแอปอื่น ตามข้อมูลจาก Android Developers, 2026 มีสามประเภทของเซอร์วิส: Started Service, Bound Service และ Foreground Service แต่ละประเภทมีวงจรชีวิตและขอบเขตการใช้งานของตนเอง
ใจความสำคัญ
Background Service (หรือเพียงแค่ Service) — หนึ่งในสี่คอมโพเนนต์หลักของแอปพลิเคชัน Android ร่วมกับ Activity, BroadcastReceiver และ ContentProvider แตกต่างจาก Activity ตรงที่ Service ไม่มีอินเทอร์เฟซภาพและออกแบบมาสำหรับการดำเนินการที่ควรดำเนินต่อไปไม่ว่าแอปจะอยู่ในเบื้องหน้าหรือไม่ก็ตาม
Service ทำงานในเธรดหลัก (main thread) ของแอปพลิเคชัน ดังนั้นการดำเนินการที่บล็อกใด ๆ ภายในจึงจำเป็นต้องสร้างเธรดแยกต่างหาก หากไม่ทำเช่นนั้น ระบบจะเรียก ANR (Application Not Responding) สำหรับการดำเนินการเบื้องหลังอย่างง่าย Android มี IntentService ซึ่งสร้างเธรดการทำงานโดยอัตโนมัติ ในโปรเจกต์สมัยใหม่แนะนำให้ใช้ Kotlin-คอรูทีนกับ CoroutineScope ภายใน Service สำหรับการประมวลผลแบบอะซิงโครนัสโดยไม่บล็อกเธรดหลัก
วัตถุประสงค์หลัก ของ Service — การเล่นเพลง การดาวน์โหลดไฟล์ การทำงานกับคำขอเครือข่าย การซิงโครไนซ์ข้อมูล และงานอื่น ๆ ที่ควรดำเนินต่อไปหลังจากผู้ใช้ออกจากแอป อย่างไรก็ตาม ด้วยการเปิดตัว Android 8 นักพัฒนาจำเป็นต้องเลือกประเภทของเซอร์วิสอย่างมีสติ โดยคำนึงถึงข้อจำกัดของการทำงานเบื้องหลัง
Service มีวงจรชีวิตของตัวเองซึ่งแตกต่างจาก Activity ประกอบด้วยสี่เมธอดหลัก: onCreate, onStartCommand, onBind และ onDestroy ความเข้าใจในวงจรนี้จำเป็นสำหรับการใช้งานงานเบื้องหลังอย่างถูกต้องโดยไม่มีการรั่วไหลของหน่วยความจำ
เมธอด onCreate ถูกเรียกเมื่อสร้างเซอร์วิส เพียงครั้งเดียวตลอดอายุของมัน ในนั้นทรัพยากรจะถูกเริ่มต้น: ตัวจับเวลา การเชื่อมต่อฐานข้อมูล ซ็อกเก็ต เมธอด onStartCommand ถูกเรียกทุกครั้งที่มีการเรียก startService ซึ่งช่วยให้สามารถส่งคำสั่งไปยังเซอร์วิสที่ทำงานอยู่แล้ว ค่าที่ส่งคืนกำหนดพฤติกรรมของระบบเมื่อรีสตาร์ท
class DownloadService : Service() {
override fun onCreate() {
super.onCreate()
initializeDownloader()
}
override fun onStartCommand(
intent: Intent?,
flags: Int,
startId: Int
): Int {
downloadFile(intent?.getStringExtra("url"))
return START_STICKY
}
override fun onBind(intent: Intent): IBinder? = null
}
onBind ถูกเรียกเมื่อผูกเซอร์วิสผ่าน bindService และส่งคืนออบเจ็กต์ IBinder สำหรับการโต้ตอบกับไคลเอนต์ เมธอดนี้ใช้สำหรับ Bound Service เท่านั้น onDestroy — การเรียกครั้งสุดท้ายก่อนที่เซอร์วิสจะถูกทำลาย ในนั้นทรัพยากรทั้งหมดจะถูกปล่อย เธรดที่หยุด และงานที่ถูกยกเลิก
Android มีสามประเภทของ Service แต่ละประเภทออกแบบมาสำหรับสถานการณ์ของตนเอง การเลือกประเภทที่ไม่ถูกต้องอาจทำให้แอปทำงานไม่เสถียรหรือสิ้นเปลืองแบตเตอรี่
Started Service เริ่มต้นด้วยการเรียก startService และทำงานจนกว่าจะเรียก stopSelf หรือ stopService เหมาะสำหรับงานที่ต้องดำเนินการทันที: การส่งข้อมูลวิเคราะห์ การประมวลผลภาพ การดาวน์โหลดไฟล์เดียว หลังจากทำงานเสร็จ เซอร์วิสจะหยุดตัวเอง
Bound Service ให้อินเทอร์เฟซแบบไคลเอนต์-เซิร์ฟเวอร์ ช่วยให้ Activity, Fragment หรือคอมโพเนนต์อื่นโต้ตอบกับเซอร์วิส เซอร์วิสมีชีวิตอยู่ตราบเท่าที่ยังมีไคลเอนต์ที่เชื่อมต่ออย่างน้อยหนึ่งตัว เมื่อไคลเอนต์ทั้งหมดยกเลิกการเชื่อมต่อ เซอร์วิสจะถูกทำลาย Bound Service สะดวกสำหรับงานที่ต้องการการสื่อสารสองทาง: เครื่องเล่นเพลง การนำทาง
Foreground Service — คือ Started Service ที่มีการแจ้งเตือนถาวรในแถบสถานะ ระบบถือว่าเซอร์วิสดังกล่าวทำงานอยู่และไม่ฆ่ามันแม้ในขณะที่หน่วยความจำไม่เพียงพอ Foreground Service จำเป็นสำหรับการเล่นเพลง การบันทึกเสียง การติดตามตำแหน่งที่ตั้ง และงานอื่น ๆ ที่สำคัญต่อผู้ใช้
| พารามิเตอร์ | Started | Bound | Foreground |
|---|---|---|---|
| การเริ่ม | startService | bindService | startForeground |
| อายุ | จนถึง stopSelf | ตราบเท่าที่ยังมีไคลเอนต์ | จนถึง stopForeground |
| การแจ้งเตือน | ไม่มี | ไม่มี | จำเป็น |
| ถูกฆ่า | ใช่ | ใช่ | ไม่ |
| ตัวอย่าง | การดาวน์โหลด | เครื่องเล่น | เพลง |
การสร้าง เซอร์วิสเริ่มต้นด้วยการประกาศคลาสที่สืบทอดจาก Service และลงทะเบียนใน AndroidManifest.xml หากไม่ลงทะเบียนในแมนิเฟสต์ ระบบจะไม่สามารถเริ่มเซอร์วิสได้ และการเรียก startService ใด ๆ จะทำให้เกิดข้อยกเว้น
// การลงทะเบียนใน AndroidManifest.xml
@SuppressLint("ForegroundServiceType")
class SyncService : Service() {
override fun onStartCommand(
intent: Intent?,
flags: Int,
startId: Int
): Int {
startForeground(
NOTIFICATION_ID,
createNotification()
)
performSync(intent)
return START_NOT_STICKY
}
}
ในการเริ่มเซอร์วิสจาก Activity หรือ Fragment จะใช้ Intent โดยระบุคลาสของเซอร์วิสอย่างชัดเจน ตั้งแต่ Android 8 สำหรับ Foreground Service จำเป็นต้องมีสิทธิ์ FOREGROUND_SERVICE ในแมนิเฟสต์
// การเริ่ม Started Service
val intent = Intent(this, SyncService::class.java)
intent.putExtra("action", "sync")
startService(intent)
// การเริ่ม Foreground Service (Android 8+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent)
} else {
startService(intent)
}
ตั้งแต่ Android 8 (API 26) Google ได้นำข้อจำกัดที่เข้มงวดสำหรับเซอร์วิสเบื้องหลัง การเริ่ม Service ในเบื้องหลัง (เมื่อแอปไม่อยู่ในเบื้องหน้า) อนุญาตเฉพาะในกรณีพิเศษ: เมื่อได้รับการแจ้งเตือนแบบ push หลังจากอุปกรณ์บูต หรือผ่าน JobScheduler
สำหรับงานที่ยาวนานซึ่งไม่ต้องการการดำเนินการทันที แนะนำให้ใช้ WorkManager หรือ JobScheduler หากแอปต้องการเซอร์วิสที่ทำงานอยู่จริง ๆ วิธีเดียวคือ Foreground Service ที่มีการแจ้งเตือนซึ่งผู้ใช้สามารถเห็นได้ การเริ่มเซอร์วิสโดยไม่มีการแจ้งเตือนในเบื้องหลังจะถูกละเว้นโดยระบบ
JobIntentService — คือคลาสพิเศษที่ปรากฏใน support library สำหรับทำงานบน Android 5+ มันรวมพฤติกรรมของ IntentService (เธรดการทำงานอัตโนมัติ การประมวลผลตามลำดับ) เข้ากับการจัดกำหนดการผ่าน JobScheduler บน Android 8+ JobIntentService ใช้ JobScheduler ภายใต้ฝาครอบ ในขณะที่เวอร์ชันเก่ากว่าใช้ Service ปกติ ซึ่งช่วยให้ประมวลผลงานเบื้องหลังอย่างสม่ำเสมอโดยไม่ต้องตรวจสอบเวอร์ชัน Android เพิ่มเติม
class UploadJobService : JobIntentService() {
companion object {
private const val JOB_ID = 1000
fun enqueueWork(context: Context, work: Intent) {
enqueueWork(
context,
UploadJobService::class.java,
JOB_ID,
work
)
}
}
override fun onHandleWork(intent: Intent) {
val fileUri = intent.getStringExtra("file_uri")
// ทำงานในเธรดพื้นหลัง
uploadFile(fileUri)
}
}
หนึ่งในปัญหาที่พบบ่อยเมื่อทำงานกับ Background Service คือการรั่วไหลของหน่วยความจำ เนื่องจาก Service สามารถมีอายุยืนกว่า Activity การอ้างอิงถึง Activity ภายใน Service (ผ่าน listener, callback หรือ broadcast) ทำให้ไม่สามารถเก็บขยะสำหรับคอมโพเนนต์ UI ได้ แนะนำให้ใช้ WeakReference, ViewModel หรือ LiveData สำหรับการเชื่อมต่อ Service กับ UI ใน onDestroy จำเป็นต้องยกเลิกการสมัครสมาชิกทั้งหมด หยุดเธรด และปิดเคอร์เซอร์
การเลือกระหว่าง Background Service และ WorkManager ขึ้นอยู่กับสถานการณ์ Service เหมาะสำหรับงานที่ต้องดำเนินการทันทีและต่อเนื่อง: การเล่นเพลง การบันทึกเสียง การติดตาม GPS WorkManager เหมาะกว่าสำหรับงานที่เลื่อนออกไปและรับประกัน: การซิงโครไนซ์ การส่งข้อมูลวิเคราะห์ การดาวน์โหลดล็อก WorkManager จะอยู่รอดเมื่ออุปกรณ์รีสตาร์ท ในขณะที่ Service ไม่รอด Service สามารถเป็น Foreground พร้อมการแจ้งเตือน ในขณะที่ WorkManager ทำงานเงียบ ๆ ในเบื้องหลัง ในทางปฏิบัติ นักพัฒนามักรวมทั้งสองวิธี: Foreground Service สำหรับงานสำคัญของผู้ใช้ และ WorkManager สำหรับการบำรุงรักษาเบื้องหลัง
Android 12 ได้แนะนำแฟล็ก android:foregroundServiceType ซึ่งกำหนดให้ระบุประเภทของบริการ: dataSync, camera, connectedDevice, location, mediaPlayback และอื่น ๆ การระบุประเภทที่ไม่ถูกต้องจะทำให้เกิดข้อยกเว้นเมื่อเริ่มต้น แนวทางปฏิบัตินี้ทำให้ Background Service โปร่งใสมากขึ้นสำหรับผู้ใช้และระบบ
การลงทะเบียน Service ในแมนิเฟสต์ที่ถูกต้องรวมถึงแอตทริบิวต์ exported (การเข้าถึงสำหรับแอปภายนอก), foregroundServiceType (ประเภทของบริการเบื้องหลังบน Android 12+) และ permission สำหรับ Bound Service จำเป็นต้องประกาศ android:permission="android.permission.BIND_JOB_SERVICE" สำหรับ JobIntentService หากไม่ลงทะเบียนในแมนิเฟสต์ การเรียก startService หรือ bindService ใด ๆ จะจบลงด้วยข้อยกเว้น ดังนั้นการตรวจสอบแมนิเฟสต์จึงเป็นขั้นตอนแรกในการวินิจฉัยปัญหากับ Service
คำถามที่พบบ่อย
Service ทำงานในเธรดหลัก (UI Thread) ของแอปพลิเคชัน การดำเนินการที่บล็อกใด ๆ ภายใน onStartCommand หรือ onHandleIntent ต้องถูกย้ายไปยังเธรดแยกหรือคอรูทีน มิฉะนั้นระบบจะเรียก ANR ภายใน 5 วินาที
IntentService — คือคลาสที่สืบทอดจาก Service ซึ่งสร้างเธรดการทำงานโดยอัตโนมัติและประมวลผลคำสั่งตามลำดับ หลังจากงานสุดท้ายเสร็จสิ้น IntentService จะหยุดตัวเอง ตั้งแต่ Android 8 เป็นต้นไป IntentService ถือว่าล้าสมัย โดยแนะนำให้ใช้ JobIntentService หรือ WorkManager แทน
การเริ่ม Started Service จากเบื้องหลังบน Android 12 เป็นสิ่งต้องห้าม ข้อยกเว้นคือ Foreground Service ที่ประกาศ foregroundServiceType ในแมนิเฟสต์และมีการแจ้งเตือนที่ถูกต้อง นอกจากนี้ยังอนุญาตให้เริ่มสั้น ๆ หลังจากได้รับข้อความ FCM ที่มีความสำคัญสูง
มีสามวิธี: BroadcastReceiver พร้อม broadcast ท้องถิ่น, กลไก Messenger ผ่าน Handler, และ LiveData/Flow ในสถาปัตยกรรม MVVM พร้อม ViewModel ร่วมกัน สำหรับ Bound Service ใช้ IBinder พร้อมการเรียกเมธอดโดยตรง
หาก Service ถูกเริ่มด้วยแฟล็ก START_STICKY ระบบจะรีสตาร์ทมันหลังจากกระบวนการถูกทำลายเมื่อหน่วยความจำไม่เพียงพอ แฟล็ก START_NOT_STICKY หมายความว่าระบบจะไม่รีสตาร์ทเซอร์วิส START_REDELIVER_INTENT คล้ายกับ START_STICKY แต่ส่ง Intent ล่าสุด
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม