พื้นที่เก็บข้อมูลแอปที่แคชไว้

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 1024
adb shell device_config set_sync_disabled_for_tests persistent

วิธีย้อนกลับการลบล้าง MAX_CACHED_PROCESSES

adb shell device_config delete activity_manager max_cached_processes
adb 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 ด้วย