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

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

เครื่องมือหยุดแอปที่แคชไว้จะเก็บแอปไว้ใน RAM ขณะที่ปิดแอปไว้ใน CPU หาก Android พิจารณาว่าแอปไม่ควรทำงานแต่คุณอาจต้องใช้แอปในอนาคต ระบบจะระงับกระบวนการของแอปแทนที่จะสิ้นสุดกระบวนการ ซึ่งจะ ป้องกันไม่ให้เกิด Cold Start เมื่อต้องใช้แอปอีกครั้ง

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 ระบบจะไม่ส่งเหตุการณ์การตัดอื่นๆ เนื่องจากเมื่อแอปมีสิทธิ์สำหรับเหตุการณ์เหล่านั้น ระบบจะ คาดหวังให้แอปหยุดทำงาน
  • หลังจากเข้าสู่สถานะแคชได้ไม่นาน ระบบอาจขอให้รันไทม์ของแอปทำการรวบรวมขยะเพื่อเตรียมพร้อมสำหรับการหยุดทำงานที่อาจเกิดขึ้น
  • เมื่อกระบวนการของแอปหยุดทำงาน อาจมีขั้นตอนการบีบอัดหน่วยความจำเพิ่มเติมเกิดขึ้น เช่น การเขียนหน้าที่ได้รับการแก้ไขไปยังพื้นที่เก็บข้อมูลสำรองและการสลับหน้าแบบไม่ระบุชื่อไปยัง 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

ระบบจะปิดใช้ฟีเจอร์การหยุดแอปชั่วคราวเมื่อคุณตั้งค่าสถานะ 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

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

จัดการฟีเจอร์ที่กำหนดเอง

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

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

โหมดความล้มเหลวที่พบบ่อย

เมื่อกระบวนการของแอปถูกระงับ การสื่อสารระหว่างกระบวนการ (IPC) ที่ไม่เหมาะสมหรือการกำหนดเวลางานอาจทำให้แอปสิ้นสุดการทำงานหรือมีลักษณะการทำงานที่ไม่คาดคิด

ธุรกรรมการผูกแบบซิงโครนัสกับกระบวนการที่หยุดชั่วคราว

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

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

หากต้องการหลีกเลี่ยงปัญหานี้ ให้ตรวจสอบว่าแอปไคลเอ็นต์ทิ้งการอ้างอิง IBinder ทันทีหลังจากเรียกใช้ Context.unbindService

บัฟเฟอร์ล้นของธุรกรรม Binder แบบไม่พร้อมกัน

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

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

การเรียกใช้งานที่กำหนดเวลาไว้ซ้ำๆ เมื่อเลิกตรึง

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

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

ทดสอบและแก้ปัญหาเกี่ยวกับเครื่องมือหยุดแอปชั่วคราว

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

คำสั่งเครื่องมือจัดการกิจกรรม

คุณสามารถใช้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 ด้วย