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

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

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

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

ต่อไปนี้เป็นบริการ 3 ประเภทที่แตกต่างกัน

เบื้องหน้า

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ข้อควรระวัง: เพื่อดูแลให้แอปปลอดภัย ให้ใช้ Intent แบบเจาะจงปลายทางเมื่อเริ่มใช้ Service และไม่ต้องประกาศตัวกรอง 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 ได้ จัดการ 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 และประมวลผลคำขอทั้งหมดตามลำดับซึ่งแยกจากกัน คุณเปลี่ยนโค้ดเพื่อเรียกใช้งานใน Thread Pool ได้ เช่น หากต้องการเรียกใช้คําขอหลายรายการพร้อมกัน

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

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

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

กำลังเริ่มต้นบริการ

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

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

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

เมื่อส่งคำขอให้หยุดกับ stopSelf() หรือ stopService() ระบบจะทำลายบริการทันที เท่าที่จะเป็นไปได้

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

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

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวงจรของบริการ โปรดดูส่วนด้านล่างเกี่ยวกับการจัดการวงจรของบริการ

การสร้างบริการที่มีผลผูกพัน

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

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

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

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

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

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

ส่งการแจ้งเตือนถึงผู้ใช้

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

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

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

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

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

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

  • บริการที่เริ่มต้น

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

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

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

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

การใช้ Callback ของวงจร

เช่นเดียวกับกิจกรรม บริการมีวิธี 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
    }
}

หมายเหตุ: วิธีนี้ต่างจากเมธอด Callback ของวงจรกิจกรรมตรงที่ ไม่จำเป็นต่อการเรียกใช้การใช้งาน Superclass ของเมธอด Callback เหล่านี้

รูปที่ 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() Callback) เว้นแต่บริการจะเชื่อมโยงกับลูกค้า ระบบจะทำลายรหัสเมื่อบริการหยุดทำงาน โดย onDestroy() เป็น Callback เดียวที่ได้รับ

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