Android 11 (ระดับ API 30) ขึ้นไปรองรับการหยุดทำงานของแอปที่แคชไว้ ฟีเจอร์นี้จะหยุดการดำเนินการสำหรับกระบวนการที่แคชไว้และลดการใช้ทรัพยากรโดยแอปที่ทำงานไม่ถูกต้องซึ่งอาจพยายามทำงานขณะแคชไว้
เครื่องมือหยุดทำงานของแอปที่แคชไว้จะเก็บแอปไว้ใน RAM ขณะที่ปิดแอปไว้ใน CPU หาก Android พิจารณาว่าแอปไม่ควรทำงานแต่ในอนาคตอาจจำเป็นต้องใช้ Android จะระงับกระบวนการของแอปแทนที่จะสิ้นสุดกระบวนการ ซึ่งจะ ป้องกันการเริ่มระบบใหม่เมื่อต้องใช้แอปอีกครั้ง
Android จะหยุดแอปที่แคชไว้ชั่วคราวโดยการย้ายกระบวนการของแอปไปยัง cgroup ที่หยุดชั่วคราว ซึ่งจะช่วยลดการใช้ CPU ที่ใช้งานอยู่และไม่มีการใช้งานในกรณีที่มีแอปที่แคชไว้ซึ่งใช้งานอยู่ คุณเปิดใช้การหยุดแอปชั่วคราวได้โดยใช้ค่าสถานะการกำหนดค่าระบบหรือ ตัวเลือกสำหรับนักพัฒนาแอป
ใน Android 14 (ระดับ API 34) ขึ้นไป โปรแกรมหยุดทำงานของแอปที่แคชไว้จะมีลักษณะการทำงานที่เสถียรดังต่อไปนี้
- ระบบจะหยุดกระบวนการของแอปในสถานะแคชชั่วคราว 10 วินาทีหลังจากเข้าสู่สถานะแคช
- ระบบจะยกเลิกการหยุดกระบวนการของแอปที่หยุดทำงานทันทีในระหว่างเหตุการณ์วงจร เหตุการณ์เหล่านี้รวมถึงการรับ Intent การเริ่มบริการงาน หรือผู้ใช้กลับมาใช้กิจกรรมต่อ
ActivityManagerService จัดการกระบวนการของแอปทั้งหมดและตัดสินใจเกี่ยวกับวงจรของแอป
CachedAppOptimizer มีหน้าที่ระงับกระบวนการของแอป
เมื่อกระบวนการของแอปถูกหยุดชั่วคราว เธรดทั้งหมดจะถูกระงับและไม่สามารถ
ทำงานใน CPU ได้จนกว่าจะยกเลิกการหยุดชั่วคราว ด้วยเหตุนี้ แอปจึงไม่สามารถดำเนินการเก็บขยะ (GC) และไม่สามารถตอบสนองต่อเหตุการณ์การตัดหน่วยความจำได้ ดูข้อมูลเพิ่มเติมได้ที่ ComponentCallbacks2.onTrimMemory(int)
เพื่อรองรับการเปลี่ยนแปลงนี้ ตั้งแต่ Android 14 เป็นต้นไป
- แอปที่มีอินสแตนซ์
Activityที่มองเห็นได้จะได้รับการแจ้งเตือนเกี่ยวกับTRIM_MEMORY_UI_HIDDENทันทีที่ย้ายไป ที่พื้นหลัง แอปที่อยู่ในวงจรโดยไม่มี UI เช่น แอป ที่มีบริการที่ทำงานอยู่เบื้องหน้า อาจได้รับTRIM_MEMORY_BACKGROUNDระบบจะไม่ส่งเหตุการณ์การตัดอื่นๆ เนื่องจากเมื่อแอปมีสิทธิ์สำหรับเหตุการณ์เหล่านั้น ระบบจะ คาดหวังให้แอปหยุดทำงาน - หลังจากเข้าสู่สถานะแคชได้ไม่นาน ระบบอาจขอให้รันไทม์ของแอป ทำการ GC เพื่อเตรียมพร้อมสำหรับการหยุดทำงานที่อาจเกิดขึ้น
- เมื่อกระบวนการของแอปหยุดทำงาน อาจมีขั้นตอนการบีบอัดหน่วยความจำเพิ่มเติมเกิดขึ้น เช่น การเขียนหน้าที่มีการแก้ไขไปยังพื้นที่เก็บข้อมูลสำรองและการสลับหน้าแบบไม่ระบุชื่อไปยัง ZRAM
- หากกระบวนการทั้งหมดของแอปหนึ่งๆ ถูกระงับ ระบบจะสิ้นสุดซ็อกเก็ต TCP ที่ใช้งานอยู่ซึ่งแอปดูแลอยู่ ซึ่งจะป้องกันไม่ให้ฝั่งเซิร์ฟเวอร์ของซ็อกเก็ตส่งการปิง Keepalive ของ TCP ที่จะปลุกโมเด็มของอุปกรณ์
ระบบจะยกเลิกการระงับกระบวนการของแอปที่แคชไว้เมื่อสถานะกระบวนการเปลี่ยนจากแคช
เป็นสถานะที่มีความสำคัญสูงขึ้น หากต้องการลดเหตุการณ์การเลิกตรึงใน Android 14 ขึ้นไป ระบบจะจัดคิวการออกอากาศที่ลงทะเบียนบริบทไว้ขณะที่แอปอยู่ในสถานะแคช การออกอากาศที่ลงทะเบียนตามบริบทคือตัวรับที่แอปจะลงทะเบียน
แบบไดนามิกโดยการเรียกใช้ Context.registerReceiver
ระบบจะส่งการออกอากาศที่อยู่ในคิวเหล่านี้หลังจากที่แอปเลิกหยุดทำงานแล้วเท่านั้น ในทางตรงกันข้าม ระบบจะไม่จัดคิวการออกอากาศที่ประกาศในไฟล์ Manifest
การออกอากาศที่ประกาศในไฟล์ Manifest คือตัวรับที่ประกาศแบบคงที่ใน
AndroidManifest.xml โดยใช้องค์ประกอบ <receiver> ระบบจะยกเลิกการตรึงแอปที่แคชไว้ทันที
เพื่อส่งการออกอากาศที่ประกาศในไฟล์ Manifest
ผลกระทบต่อสถานะของระบบ
Android จะสิ้นสุดกระบวนการแอปที่แคชไว้ซึ่งใช้งานล่าสุดน้อยที่สุดหากมีกระบวนการแอปที่แคชไว้มากกว่า MAX_CACHED_PROCESSES ในอุปกรณ์ที่รองรับซึ่งใช้ Android 14 ขึ้นไป MAX_CACHED_PROCESSES จะเพิ่มขึ้นอย่างมาก
ทำให้อุปกรณ์สามารถเก็บกระบวนการของแอปที่แคชไว้ใน RAM ได้มากขึ้น
การแคชแอปเพิ่มเติมใน RAM จะช่วยลด Cold Start ได้สูงสุด 30% โดยการลดจะปรับตาม RAM ทั้งหมดของอุปกรณ์ ในขณะเดียวกัน ระบบจะลดการใช้ CPU ของแอปที่แคชไว้ให้เหลือน้อยที่สุด ซึ่งจะช่วยประหยัดแบตเตอรี่ได้อย่างมาก
การยกเว้นตู้แช่แข็ง
ภายใต้เงื่อนไขบางอย่าง กระบวนการของแอปอาจเข้าสู่สถานะแคชแต่ยังคง ไม่หยุดทำงาน ข้อยกเว้นเหล่านี้เป็นรายละเอียดการใช้งานและอาจมีการเปลี่ยนแปลงใน Android เวอร์ชันต่อๆ ไป
- การล็อกไฟล์: หากกระบวนการที่แคชไว้มีการล็อกไฟล์ที่บล็อกกระบวนการอื่นๆ ที่ไม่ได้แคชไว้ ระบบจะไม่หยุดกระบวนการที่ล็อกไว้ชั่วคราว
- การเชื่อมโยง
BIND_WAIVE_PRIORITY: กระบวนการของแอปที่มีการเชื่อมโยงขาเข้า ซึ่งสร้างขึ้นโดยใช้Context.BIND_WAIVE_PRIORITYจะ เข้าสู่สถานะแคชได้ แต่จะยังคงไม่หยุดทำงานจนกว่ากระบวนการไคลเอ็นต์ที่เชื่อมต่อทั้งหมด จะแคชด้วย ข้อยกเว้นนี้รองรับแอปแบบหลายกระบวนการ เช่น เว็บเบราว์เซอร์ที่ใช้แท็บที่กำหนดเอง
ใช้ฟีเจอร์หยุดการทำงานของแอป
โปรแกรมหยุดทำงานของแอปที่แคชไว้จะใช้โปรแกรมหยุดทำงาน cgroup v2 ของเคอร์เนล อุปกรณ์ที่จัดส่ง
พร้อมเคอร์เนลที่เข้ากันได้จะเปิดใช้ได้ เปิดใช้ตัวเลือกสำหรับนักพัฒนาแอป Suspend
execution for cached apps หรือตั้งค่า Flag การกำหนดค่าอุปกรณ์
activity_manager_native_boot use_freezer เป็น true เช่น
adb shell device_config put activity_manager_native_boot use_freezer true && adb rebootระบบจะปิดใช้ฟีเจอร์แช่แข็งเมื่อคุณตั้งค่า Flag use_freezer เป็น false หรือ
ปิดใช้ตัวเลือกสำหรับนักพัฒนาแอป เช่น
adb shell device_config put activity_manager_native_boot use_freezer false && adb rebootคุณสามารถสลับการตั้งค่านี้ได้โดยเปลี่ยนการกำหนดค่าอุปกรณ์ในการเผยแพร่หรืออัปเดตซอฟต์แวร์
หากต้องการลบล้าง MAX_CACHED_PROCESSES เช่น ตั้งค่าเป็น 1024 สำหรับ
การทดสอบ ให้ทำดังนี้
adb shell device_config put activity_manager max_cached_processes 1024adb shell device_config set_sync_disabled_for_tests persistent
วิธีย้อนกลับการลบล้าง MAX_CACHED_PROCESSES
adb shell device_config delete activity_manager max_cached_processesadb shell device_config set_sync_disabled_for_tests none
ตั้งแต่ Android 16 (API ระดับ 36) ขึ้นไป Android จะมี API สาธารณะอย่างเป็นทางการ เช่น IBinder.FrozenStateChangeCallback และ IBinder.addFrozenStateChangeCallback
เพื่อสังเกตเมื่อกระบวนการระยะไกลถูกระงับหรือยกเลิกการระงับ คอมโพเนนต์ที่โต้ตอบกับแอปที่อาจแคชไว้สามารถใช้ API เหล่านี้เพื่อติดตามสถานะหยุดทำงานของกระบวนการระยะไกลได้
ข้อกำหนดของอุปกรณ์และเคอร์เนล
โปรแกรมหยุดทำงานของแอปที่แคชไว้ต้องรองรับ cgroup v2 ของเคอร์เนล นอกจากนี้ การแจ้งเตือนการเปลี่ยนแปลงสถานะการหยุดทำงานโดยใช้ IBinder.FrozenStateChangeCallback
ต้องมีการรองรับไดรเวอร์ Binder ของเคอร์เนล ซึ่งเป็นมาตรฐานใน Android Common
Kernels (ACK) และ Generic Kernel Images (GKI) ตั้งแต่ Android 14 (API ระดับ 34) ขึ้นไป
คุณตรวจสอบว่าอุปกรณ์รองรับความสามารถเหล่านี้หรือไม่ได้โดยใช้คำสั่งมาตรฐาน
adb ดังนี้
ตรวจสอบว่าเปิดใช้ Freezer ในอุปกรณ์แล้วหรือไม่ (สำหรับบิลด์ของผู้ใช้หรือบิลด์สำหรับแก้ไขข้อบกพร่อง)
adb shell device_config get activity_manager_native_boot use_freezerหรือตรวจสอบว่าระบบกำลังหยุดกระบวนการชั่วคราวอยู่หรือไม่ โดยทำดังนี้
adb shell dumpsys activity | grep -A 20 "Apps frozen:"ตรวจสอบการรองรับตัวควบคุมการหยุดทำงานของ cgroup v2 (สำหรับอุปกรณ์ใดก็ได้):
ตรวจสอบว่า
freezerอยู่ในรายการตัวควบคุม cgroup v2 ที่พร้อมใช้งานadb shell cat /sys/fs/cgroup/cgroup.controllersหรือในอุปกรณ์ที่รูทหรือบิลด์ userdebug ให้ตรวจสอบว่าได้ติดตั้งโหนดฟรีซเซอร์ cgroup v2 ใน cgroup ย่อยแล้ว
adb root && adb shell ls /sys/fs/cgroup/uid_0/cgroup.freezeหากมีไฟล์นี้อยู่ แสดงว่าเคอร์เนลรองรับฟีเจอร์หยุดชั่วคราวของ Cgroup v2
ตรวจสอบการรองรับการแจ้งเตือนการเปลี่ยนแปลงสถานะการหยุดทำงาน (สำหรับอุปกรณ์ใดก็ได้):
ในอุปกรณ์ที่ใช้ Android 14 ขึ้นไปซึ่งมีไดรเวอร์ Binder ของ Generic Kernel Image (GKI) ที่เข้ากันได้
IBinder.addFrozenStateChangeCallbackจะลงทะเบียนการเรียกกลับได้สำเร็จ หาก ไดรเวอร์ Binder ของเคอร์เนลพื้นฐานไม่รองรับการแจ้งเตือนการหยุดทำงาน เมธอดจะส่งUnsupportedOperationException
จัดการฟีเจอร์ที่กำหนดเอง
โดยปกติแล้วกระบวนการของแอปจะไม่ทำงานเมื่อแคชไว้ แต่บางแอปอาจมีฟีเจอร์ที่กำหนดเองซึ่งรองรับโดยกระบวนการที่คาดว่าจะทำงานขณะแคชไว้ เมื่อเปิดใช้เครื่องมือหยุดแอปชั่วคราวในอุปกรณ์ที่เรียกใช้แอปดังกล่าว ระบบจะหยุดกระบวนการที่แคชไว้ชั่วคราวและอาจทำให้ฟีเจอร์ที่กำหนดเองไม่ทำงาน
วิธีแก้ปัญหาชั่วคราวคือคุณสามารถเปลี่ยนสถานะกระบวนการเป็น "ไม่ได้แคช" ก่อนที่กระบวนการจะต้องทำงานใดๆ การเปลี่ยนแปลงนี้จะช่วยให้แอปยังคงใช้งานได้ ตัวอย่าง ของสถานะที่ใช้งานอยู่ ได้แก่ บริการที่ทำงานอยู่เบื้องหน้าที่เชื่อมโยงไว้หรือสถานะเบื้องหน้า
โหมดความล้มเหลวที่พบบ่อย
เมื่อกระบวนการของแอปถูกระงับ การสื่อสารระหว่างกระบวนการ (IPC) ที่ไม่เหมาะสมหรือการกำหนดเวลางานอาจทำให้แอปสิ้นสุดการทำงานหรือมีลักษณะการทำงานที่ไม่คาดคิด
ธุรกรรมการผูกพร้อมกันกับกระบวนการที่หยุดชั่วคราว
เมื่อกระบวนการแอปไคลเอ็นต์ส่งธุรกรรม Binder แบบซิงโครนัสไปยังกระบวนการแอปเซิร์ฟเวอร์
ที่หยุดทำงาน ระบบจะสิ้นสุดกระบวนการแอปเซิร์ฟเวอร์
ทันที ซึ่งจะช่วยป้องกันไม่ให้เธรดไคลเอ็นต์บล็อกอย่างไม่มีกำหนดขณะ
รอการตอบกลับจากเซิร์ฟเวอร์ที่หยุดทำงาน จากนั้นเธรดไคลเอ็นต์จะได้รับ
RemoteException และระบบจะทริกเกอร์
ผู้ฟังที่ลงทะเบียน ดูข้อมูลเพิ่มเติมได้ที่
IBinder.linkToDeath
สาเหตุหลัก: ความล้มเหลวนี้มักเกิดจากข้อบกพร่องในแอปไคลเอ็นต์
เมื่อไคลเอ็นต์เชื่อมโยงกับบริการ กระบวนการของเซิร์ฟเวอร์จะเชื่อมโยงกับไคลเอ็นต์และ
จะเข้าสู่สถานะแคชไม่ได้ก่อนที่ไคลเอ็นต์จะเข้า ดูข้อมูลเพิ่มเติมได้ที่ Context.bindService อย่างไรก็ตาม เมื่อไคลเอ็นต์เรียกใช้ Context.unbindService กระบวนการของเซิร์ฟเวอร์
อาจแคชและหยุดทำงาน หากไคลเอ็นต์ยังคงใช้การอ้างอิง IBinder ที่แคชไว้หลังจากยกเลิกการเชื่อมโยง ก็อาจเสี่ยงต่อการสื่อสารกับกระบวนการที่หยุดทำงาน
หากต้องการหลีกเลี่ยงปัญหานี้ ให้ตรวจสอบว่าแอปไคลเอ็นต์ทิ้งการอ้างอิง IBinder
ทันทีหลังจากเรียกใช้ Context.unbindService
จัดการการเรียกกลับระยะไกลไปยังกระบวนการของไคลเอ็นต์
บริการและคอมโพเนนต์ของระบบที่รักษา Binder Callback ที่มีอายุการใช้งานยาวนานไปยังกระบวนการไคลเอ็นต์สามารถป้องกันข้อผิดพลาดแบบซิงโครนัสและบัฟเฟอร์แบบอะซิงโครนัสล้นได้โดยการติดตามสถานะหยุดทำงานของไคลเอ็นต์
- ลงทะเบียน Listener การเปลี่ยนแปลงสถานะ: ใช้
IBinder.addFrozenStateChangeCallbackในโทเค็น Binder ของไคลเอ็นต์ขาเข้าเพื่อ รับการแจ้งเตือนเมื่อกระบวนการของไคลเอ็นต์เข้าหรือออกจาก Freezer - หยุดการเรียกใช้ชั่วคราวขณะที่หยุดชั่วคราว: เมื่อไคลเอ็นต์เข้าสู่
STATE_FROZENให้หยุดการเรียกกลับหรือการอัปเดตสถานะไปยังไคลเอ็นต์นั้นชั่วคราว - กลับมาทำงานต่อและดำเนินการเมื่อเลิกการระงับ: เมื่อลูกค้าเปลี่ยนไปใช้
STATE_UNFROZENให้กลับมาส่งการเรียกกลับและส่ง การอัปเดตแบบเป็นชุดหรือแบบรวมที่จำเป็น - ใช้ RemoteCallbackList: บริการของระบบที่ใช้
RemoteCallbackListสามารถกำหนดค่านโยบายผู้ถูกเรียกที่หยุดชั่วคราวเพื่อหยุดชั่วคราวและกลับมาส่งต่อการเรียกกลับโดยอัตโนมัติ โดยไม่ต้องดูแลตรรกะการติดตามด้วยตนเอง ดูข้อมูลเพิ่มเติมได้ที่ คำแนะนำเกี่ยวกับ Binder Freezer สำหรับบริการของระบบ
บัฟเฟอร์ล้นของธุรกรรม Binder แบบอะซิงโครนัส
เมื่อกระบวนการของแอปเซิร์ฟเวอร์ได้รับธุรกรรม Binder แบบไม่พร้อมกัน (oneway) ขณะที่หยุดชั่วคราว ระบบจะบัฟเฟอร์ธุรกรรมในบัฟเฟอร์ต่อกระบวนการ หาก
เซิร์ฟเวอร์ได้รับธุรกรรมแบบไม่พร้อมกันมากเกินไปขณะที่หยุดทำงาน บัฟเฟอร์
จะล้น และระบบจะสิ้นสุดกระบวนการแอปเซิร์ฟเวอร์
เพื่อป้องกันบัฟเฟอร์ล้นนี้ ให้หลีกเลี่ยงการส่งธุรกรรม Binder แบบอะซิงโครนัสมากเกินไปไปยังกระบวนการที่อาจแคชหรือหยุดชั่วคราว
การเรียกใช้งานที่กำหนดเวลาไว้ซ้ำๆ เมื่อเลิกตรึง
หากแอปทำงานที่ซ้ำกัน ระบบจะระงับแอปขณะที่กระบวนการ
หยุดชั่วคราว ดูข้อมูลเพิ่มเติมได้ที่
ScheduledThreadPoolExecutor.scheduleAtFixedRate
หรือ Timer.scheduleAtFixedRate เมื่อ
กระบวนการเลิกหยุดทำงาน การดำเนินการที่พลาดไปซึ่งสะสมไว้จะอาจทำงานอย่างรวดเร็ว
ต่อเนื่องกันโดยไม่มีการหน่วงเวลา
หากต้องการป้องกันไม่ให้มีการดำเนินการจำนวนมากเมื่อแอปกลับมาทำงานอีกครั้ง ให้ใช้
scheduleWithFixedDelay แทน
scheduleAtFixedRate สำหรับงานในเบื้องหลัง นอกจากนี้ คุณยังใช้ WorkManager ได้ด้วย
ทดสอบและแก้ปัญหาเกี่ยวกับเครื่องมือหยุดแอปชั่วคราว
หากต้องการยืนยันว่า App Freezer ทำงานได้ตามที่ต้องการหรือเพื่อแก้ปัญหาที่เกี่ยวข้องกับ Freezer ให้ใช้เครื่องมือและคำสั่งการวินิจฉัยต่อไปนี้
คำสั่ง Activity Manager
คุณสามารถใช้adb shell amคำสั่งเพื่อควบคุมการหยุดชั่วคราวและการบีบอัดด้วยตนเอง
สำหรับกระบวนการที่เฉพาะเจาะจงได้
บังคับให้กระบวนการหยุดทำงาน
adb shell am freeze <process>บังคับให้กระบวนการเลิกตรึง
adb shell am unfreeze <process>บังคับให้กระบวนการบีบอัดหน่วยความจำทั้งหมด
adb shell am compact full <process>
การตรวจสอบ Logcat
ดู Logcat เพื่อดูรายการที่หยุดทำงานและกลับมาทำงานทุกครั้งที่กระบวนการย้ายข้อมูลเข้าหรือออกจาก Freezer โดยทำดังนี้
adb logcat | grep -i "\(freezing\|froze\)"เอาต์พุตของบันทึกเหตุผลในการเลิกตรึงจะแสดงค่าที่แจงนับจาก enum UnfreezeReason Protocol
Buffer
การตรวจสอบ Dumpsys
ตรวจสอบรายการกระบวนการที่หยุดทำงานโดยใช้ dumpsys activity:
adb shell dumpsys activity | grep -A 20 "Apps frozen:"ตรวจสอบว่ามีไฟล์ /sys/fs/cgroup/uid_0/cgroup.freeze
ApplicationExitInfo
หากต้องการค้นหาสาเหตุของการสิ้นสุดกระบวนการก่อนหน้า โปรดดู
ActivityManager.getHistoricalProcessExitReasons
หากกระบวนการของแอปสิ้นสุดลงเนื่องจากปัญหาที่เกี่ยวข้องกับฟีเจอร์การหยุดทำงานชั่วคราว เช่น
ได้รับธุรกรรม Binder แบบซิงโครนัสขณะหยุดทำงานชั่วคราว ระบบจะตั้งค่าเหตุผลในการออกเป็น
ApplicationExitInfo.REASON_FREEZER
การติดตาม Perfetto
ระบบจะปล่อยเหตุการณ์ที่เกี่ยวข้องกับ Freezer ไปยังแทร็กชื่อ Freezer ภายใต้กระบวนการ
system_server ในการติดตาม Perfetto ดังนี้
- ชิ้นส่วน
FreezeและUnfreezeจะระบุเวลาที่กระบวนการเปลี่ยนสถานะ - เหตุการณ์
updateAppFreezeStateLSPจะแสดงเมื่อเซิร์ฟเวอร์ระบบตรวจสอบแอตทริบิวต์ของกระบวนการอีกครั้งเพื่อตัดสินใจว่าจะหยุดหรือยกเลิกการหยุด
คุณตรวจสอบเหตุการณ์เหล่านี้ได้โดยตรงใน UI ของ Perfetto หรือวิเคราะห์โดยใช้ PerfettoSQL ดังนี้
INCLUDE PERFETTO MODULE slices.with_context;
SELECT *
FROM process_slice
WHERE process_name = "system_server"
AND track_name = "Freezer"
AND (name LIKE "Freeze %" OR name LIKE "Unfreeze %");
ในไลบรารีมาตรฐาน PerfettoSQL เหตุการณ์การหยุดชั่วคราวจะสรุปไว้ในตาราง
android_freezer_events ด้วย