ภาพรวมของบริการ

Service คือคอมโพเนนต์แอปพลิเคชันที่ดําเนินการต่อเนื่องเป็นเวลานานในเบื้องหลังได้ โดยไม่มีอินเทอร์เฟซผู้ใช้ เมื่อเริ่มต้นแล้ว บริการอาจทํางานต่อไปอีกระยะหนึ่ง แม้ว่าผู้ใช้จะเปลี่ยนไปใช้แอปพลิเคชันอื่นแล้วก็ตาม นอกจากนี้ คอมโพเนนต์ยังเชื่อมโยงกับบริการเพื่อโต้ตอบกับบริการนั้นและดำเนินการการสื่อสารระหว่างกระบวนการ (IPC) ได้ด้วย ตัวอย่างเช่น บริการสามารถจัดการธุรกรรมเครือข่าย เล่นเพลง ดำเนินการ I/O ของไฟล์ หรือโต้ตอบกับผู้ให้บริการเนื้อหาได้ทั้งหมดจากเบื้องหลัง

ข้อควรระวัง: บริการจะทำงานในเธรดหลักของกระบวนการโฮสติ้ง บริการจะไม่สร้างเธรดของตัวเองและจะไม่ทำงานในกระบวนการแยกต่างหาก เว้นแต่คุณจะระบุไว้เป็นอย่างอื่น คุณควรเรียกใช้การดำเนินการที่บล็อกในเธรดแยกต่างหากภายในบริการเพื่อหลีกเลี่ยงข้อผิดพลาด "แอปพลิเคชันไม่ตอบสนอง" (ANR)

ประเภทบริการ

บริการ 3 ประเภทดังกล่าวมีดังนี้

เบื้องหน้า

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

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

ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีกำหนดค่าบริการที่ทำงานอยู่เบื้องหน้าในแอป

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

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

หมายเหตุ: หากแอปกำหนดเป้าหมายเป็น API ระดับ 26 ขึ้นไป ระบบจะจำกัดบริการที่ทำงานอยู่เบื้องหลังเมื่อแอปไม่ได้อยู่เบื้องหน้า ตัวอย่างเช่น ในกรณีส่วนใหญ่ คุณไม่ควรเข้าถึงข้อมูลตำแหน่งจากเบื้องหลัง แต่ให้กำหนดเวลางานโดยใช้ WorkManager แทน

เชื่อมโยงแล้ว
บริการจะเชื่อมโยงเมื่อคอมโพเนนต์แอปพลิเคชันเชื่อมโยงกับบริการดังกล่าวโดยการเรียกใช้ bindService() บริการที่เชื่อมโยงมีอินเทอร์เฟซไคลเอ็นต์-เซิร์ฟเวอร์ที่ช่วยให้คอมโพเนนต์โต้ตอบกับบริการ ส่งคําขอ รับผลลัพธ์ และทําสิ่งต่างๆ ดังกล่าวข้ามโปรเซสด้วยการสื่อสารระหว่างโปรเซส (IPC) บริการที่เชื่อมโยงจะทํางานก็ต่อเมื่อคอมโพเนนต์แอปพลิเคชันอื่นเชื่อมโยงกับบริการนั้น คอมโพเนนต์หลายรายการสามารถเชื่อมโยงกับบริการพร้อมกันได้ แต่เมื่อยกเลิกการเชื่อมโยงทั้งหมดแล้ว บริการก็จะถูกทำลาย

แม้ว่าโดยทั่วไปเอกสารนี้จะกล่าวถึงบริการที่เริ่มต้นและบริการที่เชื่อมโยงแยกกัน แต่บริการของคุณจะทํางานได้ 2 แบบ คือสามารถเริ่มต้น (เพื่อทํางานแบบไม่จํากัดเวลา) และอนุญาตการเชื่อมโยงได้ เพียงแค่คุณใช้เมธอดการเรียกกลับ 2 รายการหรือไม่ นั่นคือ onStartCommand() เพื่ออนุญาตให้คอมโพเนนต์เริ่มการทำงาน และ onBind() เพื่ออนุญาตให้มีการเชื่อมโยง

ไม่ว่าบริการจะเริ่มต้น เชื่อมโยง หรือทั้ง 2 อย่าง คอมโพเนนต์แอปพลิเคชันใดๆ ก็สามารถใช้บริการ (แม้กระทั่งจากแอปพลิเคชันแยกต่างหาก) ในลักษณะเดียวกับที่คอมโพเนนต์ใดๆ สามารถใช้กิจกรรมได้ โดยการเริ่มต้นด้วย Intent อย่างไรก็ตาม คุณสามารถประกาศบริการเป็นส่วนตัวในไฟล์ Manifest และบล็อกการเข้าถึงจากแอปพลิเคชันอื่นๆ ได้ โปรดดูข้อมูลเพิ่มเติมในส่วนการประกาศบริการในไฟล์ Manifest

การเลือกระหว่างบริการกับชุดข้อความ

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

หากต้องทํางานนอกเธรดหลัก แต่ทําเฉพาะในขณะที่ผู้ใช้โต้ตอบกับแอปพลิเคชัน คุณควรสร้างเธรดใหม่ในบริบทของคอมโพเนนต์แอปพลิเคชันอื่นแทน ตัวอย่างเช่น หากต้องการเล่นเพลงเฉพาะในขณะที่กิจกรรมทำงานอยู่ คุณอาจสร้างเธรดใน onCreate() เริ่มเธรดใน onStart() และหยุดเธรดใน onStop() นอกจากนี้ ให้พิจารณาใช้พูลเธรดและโปรแกรมดำเนินการจากแพ็กเกจ java.util.concurrent หรือ โคโริวทีน Kotlin แทนคลาส Thread แบบดั้งเดิม ดูข้อมูลเพิ่มเติมเกี่ยวกับการย้ายการดำเนินการไปยังเธรดเบื้องหลังได้ในเอกสารการแยกเธรดใน Android

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

ข้อมูลเบื้องต้น

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

onStartCommand()
ระบบจะเรียกใช้เมธอดนี้โดยการเรียก startService() เมื่อคอมโพเนนต์อื่น (เช่น กิจกรรม) ขอให้เริ่มบริการ เมื่อเรียกใช้เมธอดนี้ ระบบจะเริ่มบริการและสามารถทำงานในเบื้องหลังได้แบบไม่จำกัด หากคุณใช้วิธีนี้ คุณมีหน้าที่รับผิดชอบในการหยุดบริการเมื่อดำเนินการเสร็จสิ้นแล้วโดยโทรไปที่ stopSelf() หรือ stopService() หากต้องการระบุการเชื่อมโยงเท่านั้น คุณไม่จำเป็นต้องใช้วิธีการนี้
onBind()
ระบบจะเรียกใช้เมธอดนี้โดยการเรียก bindService() เมื่อคอมโพเนนต์อื่นต้องการเชื่อมโยงกับบริการ (เช่น เพื่อดำเนินการ RPC) ในการใช้งานเมธอดนี้ คุณต้องระบุอินเทอร์เฟซที่ลูกค้าใช้เพื่อสื่อสารกับบริการโดยแสดงผล IBinder คุณต้องใช้งานเมธอดนี้เสมอ แต่หากไม่ต้องการอนุญาตการเชื่อมโยง คุณควรแสดงผลเป็นค่า null
onCreate()
ระบบจะเรียกใช้เมธอดนี้เพื่อดำเนินการตามขั้นตอนการตั้งค่าแบบครั้งเดียวเมื่อสร้างบริการเป็นครั้งแรก (ก่อนที่จะเรียกใช้ onStartCommand() หรือ onBind()) หากบริการทำงานอยู่แล้ว ระบบจะไม่เรียกใช้เมธอดนี้
onDestroy()
ระบบจะเรียกใช้เมธอดนี้เมื่อไม่มีการใช้บริการอีกต่อไปและกำลังถูกทำลาย บริการของคุณควรใช้วิธีนี้เพื่อล้างทรัพยากร เช่น เทรด โปรแกรมฟังที่ลงทะเบียนไว้ หรือตัวรับ นี่คือการเรียกใช้ครั้งสุดท้ายที่บริการได้รับ

หากคอมโพเนนต์เริ่มบริการด้วยการเรียกใช้ startService() (ซึ่งส่งผลให้มีการเรียกใช้ onStartCommand()) บริการจะยังคงทำงานต่อไปจนกว่าจะหยุดด้วยตนเองด้วย stopSelf() หรือคอมโพเนนต์อื่นหยุดบริการด้วยการเรียกใช้ stopService()

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

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

ในส่วนต่อไปนี้ คุณจะเห็นวิธีสร้างเมธอดบริการ startService() และ bindService() รวมถึงวิธีใช้เมธอดเหล่านั้นจากคอมโพเนนต์แอปพลิเคชันอื่นๆ

การประกาศบริการในไฟล์ Manifest

คุณต้องประกาศบริการทั้งหมดในไฟล์ Manifest ของแอปพลิเคชัน เช่นเดียวกับที่ประกาศสำหรับกิจกรรมและคอมโพเนนต์อื่นๆ

หากต้องการประกาศบริการ ให้เพิ่มองค์ประกอบ <service> เป็นองค์ประกอบย่อยขององค์ประกอบ <application> มีตัวอย่างดังต่อไปนี้

<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>

ดูข้อมูลเพิ่มเติมเกี่ยวกับการประกาศบริการในไฟล์ Manifest ได้ที่การอ้างอิงองค์ประกอบ <service>

ยังมีแอตทริบิวต์อื่นๆ ที่คุณใส่ไว้ในองค์ประกอบ <service> เพื่อกำหนดพร็อพเพอร์ตี้ได้ เช่น สิทธิ์ที่จําเป็นในการเริ่มบริการและกระบวนการที่บริการควรทํางาน แอตทริบิวต์ android:name เป็นแอตทริบิวต์เดียวที่ต้องระบุ ซึ่งจะระบุชื่อคลาสของบริการ หลังจากเผยแพร่แอปพลิเคชันแล้ว โปรดอย่าเปลี่ยนชื่อนี้เพื่อหลีกเลี่ยงความเสี่ยงที่จะเกิดข้อผิดพลาดในโค้ดเนื่องจากต้องอาศัย Intent ที่ชัดเจนเพื่อเริ่มหรือเชื่อมโยงบริการ (อ่านบล็อกโพสต์สิ่งที่เปลี่ยนแปลงไม่ได้)

ข้อควรระวัง: ใช้ Intent แบบชัดแจ้งเสมอเมื่อเริ่ม Service และอย่าประกาศตัวกรอง Intent สำหรับบริการของคุณเพื่อให้แอปปลอดภัย การใช้ Intent แบบไม่เจาะจงปลายทางเพื่อเริ่มบริการเป็นอันตรายด้านความปลอดภัยเนื่องจากคุณไม่สามารถมั่นใจได้ว่าบริการใดจะตอบสนองต่อ Intent และผู้ใช้จะไม่เห็นบริการใดที่เริ่มทำงาน ตั้งแต่ Android 5.0 (API ระดับ 21) เป็นต้นไป ระบบจะแสดงข้อยกเว้นหากคุณเรียกใช้ bindService() ด้วย Intent ที่ไม่ชัด

คุณสามารถตรวจสอบว่าบริการพร้อมให้บริการสำหรับแอปของคุณเท่านั้นได้ด้วยการใส่แอตทริบิวต์ android:exported และตั้งค่าเป็น false ซึ่งจะหยุดไม่ให้แอปอื่นๆ เริ่มบริการของคุณได้อย่างมีประสิทธิภาพ แม้ว่าจะใช้ Intent แบบชัดแจ้งก็ตาม

หมายเหตุ: ผู้ใช้จะเห็นบริการที่ทำงานอยู่ในอุปกรณ์ หากเห็นบริการที่ไม่รู้จักหรือไม่เชื่อถือ ผู้ใช้สามารถหยุดบริการดังกล่าวได้ คุณต้องเพิ่มแอตทริบิวต์ android:description ลงในองค์ประกอบ <service> ในไฟล์ Manifest ของแอปเพื่อไม่ให้ผู้ใช้หยุดบริการของคุณโดยไม่ตั้งใจ ในคำอธิบาย ให้เขียนประโยคสั้นๆ ที่อธิบายว่าบริการทำอะไรและมีประโยชน์อย่างไร

การสร้างบริการที่เริ่มต้นแล้ว

บริการที่เริ่มแล้วคือบริการที่คอมโพเนนต์อื่นเริ่มโดยเรียก startService() ซึ่งจะส่งผลให้มีการเรียกใช้เมธอด onStartCommand() ของบริการ

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

คอมโพเนนต์แอปพลิเคชัน เช่น กิจกรรม สามารถเริ่มบริการได้โดยเรียกใช้ startService() และส่ง Intent ที่ระบุบริการและรวมข้อมูลสำหรับบริการที่จะใช้ บริการได้รับ Intent นี้ในเมธอด onStartCommand()

ตัวอย่างเช่น สมมติว่ากิจกรรมหนึ่งต้องบันทึกข้อมูลบางอย่างลงในฐานข้อมูลออนไลน์ กิจกรรมดังกล่าวสามารถเริ่มบริการที่ใช้ร่วมกันและส่งข้อมูลให้บันทึกได้โดยส่ง Intent ไปยัง startService() บริการจะรับ Intent ใน onStartCommand(), เชื่อมต่อกับอินเทอร์เน็ต และทำธุรกรรมฐานข้อมูล เมื่อธุรกรรมเสร็จสมบูรณ์แล้ว บริการจะหยุดทำงานและถูกทำลาย

ข้อควรระวัง: บริการจะทํางานในกระบวนการเดียวกับแอปพลิเคชันที่ประกาศบริการนั้นไว้ และในเธรดหลักของแอปพลิเคชันนั้นโดยค่าเริ่มต้น หากบริการของคุณทํางานอย่างหนักหน่วงหรือบล็อกขณะที่ผู้ใช้โต้ตอบกับกิจกรรมจากแอปพลิเคชันเดียวกัน บริการจะทําให้ประสิทธิภาพของกิจกรรมช้าลง เริ่มชุดข้อความใหม่ภายในบริการเพื่อไม่ให้ประสิทธิภาพของแอปพลิเคชันได้รับผลกระทบ

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

เฟรมเวิร์ก Android ยังมีIntentServiceคลาสย่อยของ Service ที่ใช้เธรดสำหรับงานเพื่อจัดการคําขอเริ่มต้นทั้งหมดทีละรายการ ไม่แนะนําให้ใช้คลาสนี้กับแอปใหม่ เนื่องจากจะทํางานได้ไม่ดีใน Android 8 Oreo เป็นต้นไป เนื่องด้วยการเปิดตัวขีดจํากัดการดําเนินการในเบื้องหลัง นอกจากนี้ ระบบจะเลิกใช้งานฟีเจอร์นี้ตั้งแต่ Android 11 เป็นต้นไป คุณสามารถใช้ JobIntentService แทน IntentService ที่ใช้งานร่วมกับ Android เวอร์ชันใหม่ได้

ส่วนต่อไปนี้จะอธิบายวิธีใช้บริการที่กําหนดเองของคุณเอง แต่คุณควรพิจารณาใช้ WorkManager แทนสําหรับ Use Case ส่วนใหญ่ โปรดดูคำแนะนำเกี่ยวกับการประมวลผลในเบื้องหลังบน Android เพื่อดูว่ามีโซลูชันที่เหมาะกับความต้องการของคุณหรือไม่

การขยายคลาส Service

คุณสามารถขยายคลาส Service เพื่อจัดการ Intent ที่เข้ามาแต่ละรายการ การติดตั้งใช้งานขั้นพื้นฐานอาจมีลักษณะดังนี้

Kotlin

class HelloService : Service() {

    private var serviceLooper: Looper? = null
    private var serviceHandler: ServiceHandler? = null

    // Handler that receives messages from the thread
    private inner class ServiceHandler(looper: Looper) : Handler(looper) {

        override fun handleMessage(msg: Message) {
            // Normally we would do some work here, like download a file.
            // For our sample, we just sleep for 5 seconds.
            try {
                Thread.sleep(5000)
            } catch (e: InterruptedException) {
                // Restore interrupt status.
                Thread.currentThread().interrupt()
            }

            // Stop the service using the startId, so that we don't stop
            // the service in the middle of handling another job
            stopSelf(msg.arg1)
        }
    }

    override fun onCreate() {
        // Start up the thread running the service.  Note that we create a
        // separate thread because the service normally runs in the process's
        // main thread, which we don't want to block.  We also make it
        // background priority so CPU-intensive work will not disrupt our UI.
        HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND).apply {
            start()

            // Get the HandlerThread's Looper and use it for our Handler
            serviceLooper = looper
            serviceHandler = ServiceHandler(looper)
        }
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show()

        // For each start request, send a message to start a job and deliver the
        // start ID so we know which request we're stopping when we finish the job
        serviceHandler?.obtainMessage()?.also { msg ->
            msg.arg1 = startId
            serviceHandler?.sendMessage(msg)
        }

        // If we get killed, after returning from here, restart
        return START_STICKY
    }

    override fun onBind(intent: Intent): IBinder? {
        // We don't provide binding, so return null
        return null
    }

    override fun onDestroy() {
        Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show()
    }
}

Java

public class HelloService extends Service {
  private Looper serviceLooper;
  private ServiceHandler serviceHandler;

  // Handler that receives messages from the thread
  private final class ServiceHandler extends Handler {
      public ServiceHandler(Looper looper) {
          super(looper);
      }
      @Override
      public void handleMessage(Message msg) {
          // Normally we would do some work here, like download a file.
          // For our sample, we just sleep for 5 seconds.
          try {
              Thread.sleep(5000);
          } catch (InterruptedException e) {
              // Restore interrupt status.
              Thread.currentThread().interrupt();
          }
          // Stop the service using the startId, so that we don't stop
          // the service in the middle of handling another job
          stopSelf(msg.arg1);
      }
  }

  @Override
  public void onCreate() {
    // Start up the thread running the service. Note that we create a
    // separate thread because the service normally runs in the process's
    // main thread, which we don't want to block. We also make it
    // background priority so CPU-intensive work doesn't disrupt our UI.
    HandlerThread thread = new HandlerThread("ServiceStartArguments",
            Process.THREAD_PRIORITY_BACKGROUND);
    thread.start();

    // Get the HandlerThread's Looper and use it for our Handler
    serviceLooper = thread.getLooper();
    serviceHandler = new ServiceHandler(serviceLooper);
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

      // For each start request, send a message to start a job and deliver the
      // start ID so we know which request we're stopping when we finish the job
      Message msg = serviceHandler.obtainMessage();
      msg.arg1 = startId;
      serviceHandler.sendMessage(msg);

      // If we get killed, after returning from here, restart
      return START_STICKY;
  }

  @Override
  public IBinder onBind(Intent intent) {
      // We don't provide binding, so return null
      return null;
  }

  @Override
  public void onDestroy() {
    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
  }
}

โค้ดตัวอย่างจัดการสายเรียกเข้าทั้งหมดใน onStartCommand() และโพสต์งานไปยัง Handler ที่ทำงานบนเธรดในเบื้องหลัง โดยจะทํางานเหมือนกับ IntentService และประมวลผลคําขอทั้งหมดตามลําดับทีละรายการ คุณอาจเปลี่ยนโค้ดให้ทำงานในพูลเธรด เช่น หากต้องการเรียกใช้คําขอหลายรายการพร้อมกัน

โปรดทราบว่าเมธอด onStartCommand() ต้องแสดงผลลัพธ์เป็นจำนวนเต็ม จำนวนเต็มคือค่าที่อธิบายวิธีที่ระบบควรให้บริการต่อไปในกรณีที่ระบบหยุดบริการ ค่าที่แสดงผลจาก onStartCommand() ต้องเป็นค่าคงที่ค่าใดค่าหนึ่งต่อไปนี้

START_NOT_STICKY
หากระบบหยุดบริการหลังจากที่ onStartCommand() แสดงผล อย่าสร้างบริการขึ้นมาใหม่ เว้นแต่จะมีความตั้งใจที่จะแสดงผลที่รอดำเนินการอยู่ ตัวเลือกนี้เป็นตัวเลือกที่ปลอดภัยที่สุดในการหลีกเลี่ยงการเรียกใช้บริการเมื่อไม่จำเป็น และเมื่อแอปพลิเคชันสามารถเริ่มงานที่ยังทำไม่เสร็จอีกครั้งได้
START_STICKY
หากระบบหยุดบริการหลังจากที่ onStartCommand() แสดงผล ให้สร้างบริการขึ้นมาใหม่และเรียกใช้ onStartCommand() แต่อย่าส่ง Intent สุดท้ายอีกครั้ง แต่ระบบจะเรียก onStartCommand() ด้วย Intent แบบ Null แทน เว้นแต่จะมี Intent ที่รอดำเนินการเพื่อเริ่มบริการ ในกรณีดังกล่าว ระบบจะส่ง Intent เหล่านั้น ซึ่งเหมาะสำหรับโปรแกรมเล่นสื่อ (หรือบริการที่คล้ายกัน) ที่ไม่ได้ดำเนินการตามคําสั่ง แต่ทํางานอยู่อย่างไม่มีกำหนดและรองาน
START_REDELIVER_INTENT
หากระบบหยุดบริการหลังจากที่ onStartCommand() แสดงผล ให้สร้างบริการขึ้นมาใหม่และเรียกใช้ onStartCommand() ด้วย Intent สุดท้ายที่ส่งไปยังบริการ ระบบจะแสดง Intent ที่รอดำเนินการตามลำดับ ซึ่งเหมาะสำหรับบริการที่ทำงานอยู่อย่างต่อเนื่องและควรกลับมาทำงานต่อทันที เช่น การดาวน์โหลดไฟล์

ดูรายละเอียดเพิ่มเติมเกี่ยวกับค่าที่แสดงผลเหล่านี้ได้ในเอกสารอ้างอิงแบบลิงก์สำหรับค่าคงที่แต่ละรายการ

การเริ่มบริการ

คุณเริ่มบริการจากกิจกรรมหรือคอมโพเนนต์แอปพลิเคชันอื่นๆ ได้โดยการส่ง Intent ไปยัง startService() หรือ startForegroundService() ระบบ Android จะเรียกใช้เมธอด onStartCommand() ของบริการและส่ง Intent ไปให้ ซึ่งจะระบุบริการที่จะเริ่มต้น

หมายเหตุ: หากแอปกำหนดเป้าหมายเป็น API ระดับ 26 ขึ้นไป ระบบจะจำกัดการใช้หรือสร้างบริการที่ทำงานอยู่เบื้องหลัง เว้นแต่แอปจะทำงานอยู่เบื้องหน้า หากแอปต้องการสร้างบริการที่ทำงานอยู่เบื้องหน้า แอปควรเรียกใช้ startForegroundService() เมธอดดังกล่าวจะสร้างบริการในเบื้องหลัง แต่เมธอดจะส่งสัญญาณให้ระบบทราบว่าบริการจะโปรโมตตัวเองไปยังเบื้องหน้า เมื่อสร้างบริการแล้ว บริการต้องเรียกใช้เมธอด startForeground() ภายใน 5 วินาที

ตัวอย่างเช่น กิจกรรมสามารถเริ่มบริการตัวอย่างในส่วนก่อนหน้า (HelloService) โดยใช้ Intent แบบชัดแจ้งที่มี startService() ดังที่แสดงที่นี่

Kotlin

startService(Intent(this, HelloService::class.java))

Java

startService(new Intent(this, HelloService.class));

เมธอด startService() จะแสดงผลทันที และระบบ Android จะเรียกเมธอด onStartCommand() ของบริการ หากบริการไม่ได้ทำงานอยู่ ระบบจะเรียกใช้ onCreate() ก่อน แล้วจึงเรียกใช้ onStartCommand()

หากบริการไม่ได้ให้การเชื่อมโยงด้วย Intent ที่ส่งมาพร้อมกับ startService() จะเป็นโหมดการสื่อสารเพียงโหมดเดียวระหว่างคอมโพเนนต์แอปพลิเคชันกับบริการ อย่างไรก็ตาม หากต้องการให้บริการส่งผลลัพธ์กลับ ไคลเอ็นต์ที่เริ่มบริการสามารถสร้าง PendingIntent สําหรับการออกอากาศ (ที่มี getBroadcast()) และส่งไปยังบริการใน Intent ที่เริ่มบริการ จากนั้นบริการจะใช้การออกอากาศเพื่อแสดงผลลัพธ์

คำขอเริ่มบริการหลายรายการส่งผลให้มีการเรียกใช้ onStartCommand() ของบริการหลายครั้ง อย่างไรก็ตาม คุณส่งคำขอหยุดบริการ (ด้วย stopSelf() หรือ stopService()) ได้เพียงครั้งเดียว

การหยุดบริการ

บริการที่เริ่มแล้วต้องจัดการวงจรของตนเอง กล่าวคือ ระบบจะไม่หยุดหรือทำลายบริการ เว้นแต่จะต้องกู้คืนหน่วยความจำของระบบและบริการยังคงทำงานต่อไปหลังจากที่ onStartCommand() กลับมา บริการต้องหยุดเองโดยการเรียก stopSelf() หรือคอมโพเนนต์อื่นสามารถหยุดบริการได้โดยเรียก stopService()

เมื่อมีการขอให้หยุดด้วย stopSelf() หรือ stopService() ระบบจะทำลายบริการโดยเร็วที่สุด

หากบริการจัดการคําขอ onStartCommand() หลายรายการพร้อมกัน คุณไม่ควรหยุดบริการเมื่อประมวลผลคําขอเริ่มแล้ว เนื่องจากคุณอาจได้รับคําขอเริ่มใหม่ (การหยุดเมื่อสิ้นสุดคําขอแรกจะเป็นการสิ้นสุดคําขอที่ 2) หากต้องการหลีกเลี่ยงปัญหานี้ คุณสามารถใช้ stopSelf(int) เพื่อให้มั่นใจว่าคำขอหยุดบริการจะอิงตามคำขอเริ่มล่าสุดเสมอ กล่าวคือ เมื่อคุณเรียกใช้ stopSelf(int) คุณจะส่งรหัสของคำขอเริ่มต้น (startId ที่ส่งไปยัง onStartCommand()) ให้กับคำขอหยุด จากนั้น หากบริการได้รับคําขอเริ่มต้นใหม่ก่อนที่คุณจะเรียก stopSelf(int) ได้ รหัสจะไม่ตรงกัน และบริการจะไม่หยุด

ข้อควรระวัง: ตรวจสอบว่าแอปพลิเคชันหยุดให้บริการเมื่อทำงานเสร็จแล้ว เพื่อไม่ให้สิ้นเปลืองทรัพยากรของระบบและพลังงานแบตเตอรี่ คอมโพเนนต์อื่นๆ สามารถหยุดบริการได้โดยเรียกใช้ stopService() หากจำเป็น แม้ว่าคุณจะเปิดใช้การเชื่อมโยงสําหรับบริการแล้ว คุณก็ต้องหยุดบริการด้วยตนเองเสมอหากบริการได้รับการโทรไปยัง onStartCommand()

ดูข้อมูลเพิ่มเติมเกี่ยวกับวงจรชีวิตของบริการได้ที่ส่วนการจัดการวงจรชีวิตของบริการด้านล่าง

การสร้างบริการที่เชื่อมโยง

บริการที่เชื่อมโยงคือบริการที่อนุญาตให้คอมโพเนนต์แอปพลิเคชันเชื่อมโยงกับบริการดังกล่าวโดยการเรียกใช้ bindService() เพื่อสร้างการเชื่อมต่อระยะยาว โดยทั่วไปแล้วจะไม่อนุญาตให้คอมโพเนนต์เริ่มการทำงานโดยเรียกใช้ startService()

สร้างบริการที่เชื่อมโยงเมื่อคุณต้องการโต้ตอบกับบริการจากกิจกรรมและคอมโพเนนต์อื่นๆ ในแอปพลิเคชัน หรือเพื่อแสดงฟังก์ชันบางอย่างของแอปพลิเคชันต่อแอปพลิเคชันอื่นๆ ผ่านการติดต่อระหว่างกระบวนการ (IPC)

หากต้องการสร้างบริการที่เชื่อมโยง ให้ใช้เมธอดการเรียกกลับ onBind() เพื่อแสดงผล IBinder ที่กำหนดอินเทอร์เฟซสำหรับการสื่อสารกับบริการ จากนั้นคอมโพเนนต์แอปพลิเคชันอื่นๆ จะเรียกใช้ bindService() เพื่อดึงข้อมูลอินเทอร์เฟซและเริ่มเรียกใช้เมธอดในบริการ บริการจะทำงานเพื่อให้บริการคอมโพเนนต์แอปพลิเคชันที่เชื่อมโยงกับบริการเท่านั้น ดังนั้นเมื่อไม่มีคอมโพเนนต์ที่เชื่อมโยงกับบริการ ระบบก็จะทำลายบริการนั้น คุณไม่จําเป็นต้องหยุดบริการที่เชื่อมโยงในลักษณะเดียวกับที่ต้องทำเมื่อเริ่มบริการผ่าน onStartCommand()

หากต้องการสร้างบริการที่เชื่อมโยง คุณต้องกำหนดอินเทอร์เฟซที่ระบุวิธีที่ไคลเอ็นต์สื่อสารกับบริการได้ อินเทอร์เฟซระหว่างบริการกับไคลเอ็นต์นี้ต้องเป็นการใช้งาน IBinder และเป็นสิ่งที่บริการของคุณต้องแสดงผลจากเมธอดการเรียกกลับ onBind() หลังจากได้รับ IBinder แล้ว ลูกค้าจะเริ่มโต้ตอบกับบริการผ่านอินเทอร์เฟซนั้นได้

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

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

การส่งการแจ้งเตือนไปยังผู้ใช้

เมื่อบริการทํางานอยู่ บริการจะแจ้งเตือนผู้ใช้เกี่ยวกับเหตุการณ์ได้โดยใช้การแจ้งเตือนแถบแสดงข้อความหรือการแจ้งเตือนแถบสถานะ

การแจ้งเตือนในแถบนําทางคือข้อความที่ปรากฏบนพื้นผิวของหน้าต่างปัจจุบันเพียงชั่วครู่ก่อนจะหายไป การแจ้งเตือนในแถบสถานะจะแสดงไอคอนในแถบสถานะพร้อมข้อความ ซึ่งผู้ใช้สามารถเลือกเพื่อดำเนินการ (เช่น เริ่มกิจกรรม)

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

การจัดการวงจรของบริการ

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

วงจรบริการตั้งแต่สร้างจนถึงทำลายอาจเป็นไปตามเส้นทางใดเส้นทางหนึ่งต่อไปนี้

  • บริการที่เริ่มแล้ว

    ระบบจะสร้างบริการเมื่อคอมโพเนนต์อื่นเรียก startService() จากนั้นบริการจะทํางานไปเรื่อยๆ และต้องหยุดตัวเองโดยเรียกใช้ stopSelf() คอมโพเนนต์อื่นยังหยุดบริการได้โดยเรียกใช้ stopService() เมื่อบริการหยุดลง ระบบจะทำลายข้อมูล

  • บริการที่มีผลผูกพัน

    ระบบจะสร้างบริการเมื่อคอมโพเนนต์อื่น (ไคลเอ็นต์) เรียกใช้ bindService() จากนั้นไคลเอ็นต์จะสื่อสารกับบริการผ่านอินเทอร์เฟซ IBinder โดยสามารถปิดการเชื่อมต่อได้โดยเรียกใช้ unbindService() ไคลเอ็นต์หลายตัวสามารถเชื่อมโยงกับบริการเดียวกันได้ และเมื่อไคลเอ็นต์ทั้งหมดยกเลิกการเชื่อมโยงแล้ว ระบบจะทำลายบริการนั้น บริการไม่จําเป็นต้องหยุดเอง

เส้นทางทั้ง 2 นี้ไม่ได้แยกจากกันโดยสิ้นเชิง คุณสามารถเชื่อมโยงกับบริการที่เริ่มต้นด้วย startService() อยู่แล้ว เช่น คุณสามารถเริ่มบริการเพลงที่เล่นอยู่เบื้องหลังได้โดยเรียกใช้ startService() พร้อม Intent ที่ระบุเพลงที่จะเล่น ต่อมา กิจกรรมจะเชื่อมโยงกับบริการได้โดยเรียกใช้ bindService() เมื่อผู้ใช้ต้องการควบคุมโปรแกรมเล่นหรือรับข้อมูลเกี่ยวกับเพลงที่เล่นอยู่ ในกรณีเช่นนี้ stopService() หรือ stopSelf() จะไม่หยุดบริการจนกว่าไคลเอ็นต์ทั้งหมดจะยกเลิกการเชื่อมโยง

การใช้ Lifecycle Callback

บริการมีเมธอดการเรียกกลับของวงจรที่คุณสามารถใช้เพื่อตรวจสอบการเปลี่ยนแปลงในสถานะของบริการและทํางานในเวลาที่เหมาะสมได้เช่นเดียวกับกิจกรรม โครงร่างบริการต่อไปนี้แสดงเมธอดวงจรชีวิตของแต่ละรายการ

Kotlin

class ExampleService : Service() {
    private var startMode: Int = 0             // indicates how to behave if the service is killed
    private var binder: IBinder? = null        // interface for clients that bind
    private var allowRebind: Boolean = false   // indicates whether onRebind should be used

    override fun onCreate() {
        // The service is being created
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // The service is starting, due to a call to startService()
        return startMode
    }

    override fun onBind(intent: Intent): IBinder? {
        // A client is binding to the service with bindService()
        return binder
    }

    override fun onUnbind(intent: Intent): Boolean {
        // All clients have unbound with unbindService()
        return allowRebind
    }

    override fun onRebind(intent: Intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }

    override fun onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

Java

public class ExampleService extends Service {
    int startMode;       // indicates how to behave if the service is killed
    IBinder binder;      // interface for clients that bind
    boolean allowRebind; // indicates whether onRebind should be used

    @Override
    public void onCreate() {
        // The service is being created
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // The service is starting, due to a call to startService()
        return startMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
        return binder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return allowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }
    @Override
    public void onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

หมายเหตุ: คุณไม่จําเป็นต้องเรียกใช้การใช้งานเมธอดการเรียกคืนเหล่านี้ของซุปเปอร์คลาส ต่างจากเมธอดการเรียกคืนของวงจรชีวิตของกิจกรรม

รูปที่ 2 วงจรชีวิตของบริการ แผนภาพด้านซ้ายแสดงวงจรเมื่อสร้างบริการด้วย startService() และแผนภาพด้านขวาแสดงวงจรเมื่อสร้างบริการด้วย bindService()

รูปที่ 2 แสดงเมธอดการเรียกกลับทั่วไปสําหรับบริการ แม้ว่ารูปภาพจะแยกบริการที่สร้างโดย startService() ออกจากบริการที่สร้างโดย bindService() แต่โปรดทราบว่าบริการใดก็ตามไม่ว่าจะเริ่มต้นอย่างไรก็อาจมีแนวโน้มที่จะอนุญาตให้ไคลเอ็นต์เชื่อมโยงกับบริการนั้น บริการที่เริ่มต้นด้วย onStartCommand() (เมื่อลูกค้าโทรหา startService()) ในตอนต้นจะยังคงรับสายที่โทรเข้ามายัง onBind() ได้ (เมื่อลูกค้าโทรหา bindService())

การใช้เมธอดเหล่านี้จะช่วยให้คุณตรวจสอบลูปที่ซ้อนกัน 2 ลูปนี้ของวงจรชีวิตของบริการได้

  • ตลอดอายุของบริการเกิดขึ้นระหว่างเวลาที่เรียก onCreate() กับเวลาที่ onDestroy() แสดงผล บริการจะตั้งค่าเริ่มต้นใน onCreate() และปล่อยทรัพยากรที่เหลือทั้งหมดใน onDestroy() เช่นเดียวกับกิจกรรม ตัวอย่างเช่น บริการเล่นเพลงสามารถสร้างเธรดที่เล่นเพลงใน onCreate() แล้วหยุดเธรดใน onDestroy()

    หมายเหตุ: ระบบจะเรียกใช้เมธอด onCreate() และ onDestroy() สำหรับบริการทั้งหมด ไม่ว่าจะสร้างโดย startService() หรือ bindService()

  • อายุการใช้งานที่ใช้งานอยู่ของบริการจะเริ่มต้นด้วยการเรียกใช้ onStartCommand() หรือ onBind() แต่ละเมธอดจะได้รับ Intent ที่ส่งไปยัง startService() หรือ bindService()

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

หมายเหตุ: แม้ว่าการเรียกใช้ stopSelf() หรือ stopService() จะหยุดบริการที่เริ่มแล้ว แต่จะไม่มีการเรียกกลับที่เกี่ยวข้องสําหรับบริการนั้น (ไม่มีการเรียกกลับ onStop()) ระบบจะทำลายบริการเมื่อหยุดบริการ เว้นแต่ว่าบริการจะเชื่อมโยงกับไคลเอ็นต์ onDestroy() จะเป็นคอลแบ็กเดียวที่ได้รับ

ดูข้อมูลเพิ่มเติมเกี่ยวกับการสร้างบริการที่มีการเชื่อมโยงได้ที่เอกสารบริการที่เชื่อมโยง ซึ่งมีข้อมูลเพิ่มเติมเกี่ยวกับเมธอด onRebind() callback ในส่วนการจัดการวงจรชีวิตของบริการที่เชื่อมโยง