ขีดจำกัดของการดำเนินการเบื้องหลัง

เมื่อใดก็ตามที่แอปทำงานอยู่เบื้องหลัง แอปจะใช้ทรัพยากรที่มีจํากัดของอุปกรณ์ เช่น RAM ซึ่งอาจส่งผลให้ผู้ใช้ได้รับประสบการณ์การใช้งานที่ไม่ดี โดยเฉพาะหากผู้ใช้กำลังใช้แอปที่ต้องใช้ทรัพยากรมาก เช่น เล่นเกมหรือดูวิดีโอ Android 8.0 (API ระดับ 26) มีข้อจำกัดเกี่ยวกับสิ่งที่แอปทำได้ขณะทำงานอยู่เบื้องหลัง เพื่อปรับปรุงประสบการณ์ของผู้ใช้ เอกสารนี้อธิบายการเปลี่ยนแปลงในระบบปฏิบัติการ และวิธีอัปเดตแอปให้ทำงานได้ดีภายใต้ข้อจำกัดใหม่

ภาพรวม

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

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

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

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

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

ข้อจำกัดของบริการที่ทำงานอยู่เบื้องหลัง

บริการที่ทำงานอยู่เบื้องหลังอาจใช้ทรัพยากรของอุปกรณ์ ซึ่งอาจส่งผลให้ผู้ใช้ได้รับประสบการณ์การใช้งานที่แย่ลง ระบบจึงใช้ข้อจำกัดบางอย่างกับบริการเพื่อลดปัญหานี้

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

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

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

ขณะที่แอปทำงานอยู่เบื้องหน้า แอปจะสร้างและเรียกใช้ทั้งบริการที่ทำงานอยู่เบื้องหน้าและเบื้องหลังได้อย่างอิสระ เมื่อแอปทำงานอยู่เบื้องหลัง แอปจะมีกรอบเวลา 2-3 นาทีที่ยังคงได้รับอนุญาตให้สร้างและใช้บริการ เมื่อสิ้นสุดกรอบเวลาดังกล่าว ระบบจะถือว่าแอปไม่ได้ใช้งาน ในเวลานี้ ระบบจะหยุดบริการเบื้องหลังของแอป เหมือนกับว่าแอปเรียกใช้เมธอด Service.stopSelf() ของบริการ

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

  • การจัดการข้อความ Firebase Cloud Messaging (FCM) ที่มีลําดับความสําคัญสูง
  • การรับการออกอากาศ เช่น ข้อความ SMS/MMS
  • การดำเนินการ PendingIntent จากการแจ้งเตือน
  • การเริ่ม VpnService ก่อนที่แอป VPN จะแสดงตัวเองขึ้นมาในเบื้องหน้า

ในหลายกรณี แอปสามารถแทนที่บริการที่ทำงานอยู่เบื้องหลังด้วยงาน JobScheduler ตัวอย่างเช่น CoolPhotoApp ต้องตรวจสอบว่าผู้ใช้ได้รับรูปภาพที่แชร์จากเพื่อนหรือไม่ แม้ว่าแอปจะไม่ทำงานอยู่เบื้องหน้าก็ตาม ก่อนหน้านี้ แอปใช้บริการเบื้องหลังซึ่งตรวจสอบกับพื้นที่เก็บข้อมูลระบบคลาวด์ของแอป หากต้องการย้ายข้อมูลไปยัง Android 8.0 (API ระดับ 26) นักพัฒนาแอปจะต้องแทนที่บริการที่ทำงานอยู่เบื้องหลังด้วยงานที่ตั้งเวลาไว้ ซึ่งจะเปิดใช้งานเป็นระยะๆ ค้นหาเซิร์ฟเวอร์ แล้วออก

ก่อน Android 8.0 วิธีทั่วไปในการสร้างบริการที่ทำงานอยู่เบื้องหน้าคือการสร้างขึ้นเป็นบริการในเบื้องหลัง จากนั้นจึงเลื่อนระดับบริการนั้นไปไว้ที่เบื้องหน้า สำหรับ Android 8.0 นั้นมีความซับซ้อน เนื่องจากระบบไม่อนุญาตให้แอปที่ทำงานอยู่เบื้องหลังสร้างบริการที่ทำงานอยู่เบื้องหลัง ด้วยเหตุนี้ Android 8.0 จึงเปิดตัวเมธอดใหม่ startForegroundService() เพื่อเริ่มบริการใหม่ในเบื้องหน้า หลังจากระบบสร้างบริการแล้ว แอปจะมีเวลา 5 วินาทีในการเรียกใช้เมธอด [startForeground()](/reference/android/app/Service#startForeground(int, android.app.Notification) ของบริการเพื่อแสดงการแจ้งเตือนที่ผู้ใช้มองเห็นของบริการใหม่ หากแอปไม่เรียกใช้ startForeground() ภายในเวลาจำกัด ระบบจะหยุดบริการและประกาศว่าแอปเป็น ANR

ข้อจำกัดของประกาศ

หากแอปลงทะเบียนเพื่อรับการออกอากาศ ผู้รับของแอปจะใช้ทรัพยากรทุกครั้งที่มีการส่งการออกอากาศ ซึ่งอาจทำให้เกิดปัญหาหากแอปลงทะเบียนเพื่อรับการออกอากาศตามเหตุการณ์ของระบบมากเกินไป เนื่องจากเหตุการณ์ของระบบที่เรียกให้เกิดการออกอากาศอาจทําให้แอปทั้งหมดเหล่านั้นใช้ทรัพยากรอย่างต่อเนื่องอย่างรวดเร็ว ซึ่งจะส่งผลเสียต่อประสบการณ์ของผู้ใช้ Android 7.0 (API ระดับ 24) ได้จำกัดการออกอากาศเพื่อลดปัญหานี้ ตามที่อธิบายไว้ในการเพิ่มประสิทธิภาพการทำงานเบื้องหลัง Android 8.0 (API ระดับ 26) ทำให้ข้อจำกัดเหล่านี้เข้มงวดยิ่งขึ้น

  • แอปที่กําหนดเป้าหมายเป็น Android 8.0 ขึ้นไปจะลงทะเบียน Broadcast Receiver สําหรับการออกอากาศโดยนัยในไฟล์ Manifest ไม่ได้อีกต่อไป เว้นแต่การออกอากาศจะจํากัดไว้สําหรับแอปนั้นโดยเฉพาะ การออกอากาศโดยนัยคือการออกอากาศที่ไม่ได้กำหนดเป้าหมายไปยังคอมโพเนนต์ที่เฉพาะเจาะจงภายในแอป เช่น ระบบจะส่ง ACTION_PACKAGE_REPLACED ไปยังผู้ฟังที่ลงทะเบียนไว้ทั้งหมดในแอปทั้งหมดเพื่อแจ้งให้ทราบว่ามีการแทนที่แพ็กเกจบางอย่างในอุปกรณ์ เนื่องจากเป็นการออกอากาศแบบไม่ระบุ จึงจะไม่ส่งไปยังตัวรับที่ลงทะเบียนในไฟล์ Manifest ในแอปที่กำหนดเป้าหมายเป็น Android 8.0 ขึ้นไป ACTION_MY_PACKAGE_REPLACED เป็นการออกอากาศโดยนัยเช่นกัน แต่เนื่องจากมีการส่งไปยังแอปที่มีการเปลี่ยนแพ็กเกจเท่านั้น ระบบจึงจะส่งไปยังผู้รับที่ลงทะเบียนในไฟล์ Manifest
  • แอปจะลงทะเบียนเพื่อออกอากาศอย่างโจ่งแจ้งในไฟล์ Manifest ได้ต่อไป
  • แอปสามารถใช้ Context.registerReceiver() ขณะรันไทม์เพื่อลงทะเบียนตัวรับสำหรับประกาศใดๆ ไม่ว่าจะโดยนัยหรือโดยชัดแจ้ง
  • การออกอากาศที่ต้องมีสิทธิ์ลายเซ็นจะได้รับการยกเว้นจากข้อจำกัดนี้ เนื่องจากระบบจะส่งการออกอากาศเหล่านี้ไปยังแอปที่ลงนามด้วยใบรับรองเดียวกันเท่านั้น ไม่ใช่ไปยังแอปทั้งหมดในอุปกรณ์

ในหลายกรณี แอปที่ลงทะเบียนสําหรับการออกอากาศโดยนัยก่อนหน้านี้จะรับฟังก์ชันการทํางานแบบเดียวกันได้โดยใช้งาน JobScheduler ตัวอย่างเช่น แอปรูปภาพโซเชียลอาจต้องล้างข้อมูลเป็นครั้งคราว และต้องการดำเนินการนี้เมื่ออุปกรณ์เชื่อมต่อกับที่ชาร์จ ก่อนหน้านี้ แอปจะลงทะเบียนตัวรับสำหรับ ACTION_POWER_CONNECTED ในไฟล์ Manifest เมื่อแอปได้รับการออกอากาศดังกล่าว ก็จะตรวจสอบว่าจำเป็นต้องล้างข้อมูลหรือไม่ หากต้องการเปลี่ยนไปใช้ Android 8.0 ขึ้นไป แอปจะนำตัวรับดังกล่าวออกจากไฟล์ Manifest แต่แอปจะตั้งเวลางานล้างข้อมูลที่จะทำงานเมื่ออุปกรณ์ไม่ได้ใช้งานและกำลังชาร์จแทน

คำแนะนำในการย้ายข้อมูล

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

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

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

ตรวจสอบตัวรับการออกอากาศที่กําหนดไว้ในไฟล์ Manifest ของแอป หากไฟล์ Manifest ประกาศตัวรับสําหรับการออกอากาศโดยนัยที่ได้รับผลกระทบ คุณต้องแทนที่ไฟล์นั้น แนวทางแก้ปัญหาที่เป็นไปได้มีดังนี้

  • สร้างตัวรับที่รันไทม์โดยเรียกใช้ Context.registerReceiver() แทนการประกาศตัวรับในไฟล์ Manifest
  • ใช้งานที่กำหนดเวลาไว้เพื่อตรวจสอบเงื่อนไขที่จะทริกเกอร์การออกอากาศโดยนัย