เครื่องมือและประสิทธิภาพ

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

ลำโพง

เกี่ยวกับ

เกี่ยวกับการเสวนา

การจัดการหน่วยความจำบิตแมปมีความสำคัญอย่างยิ่งต่อการลดความเสี่ยงของหน่วยความจำไม่เพียงพอ (OOM) ในแอป Android ที่มีภาพจำนวนมาก แม้ว่านักพัฒนาแอปจะทราบดีว่าต้องลดขนาดรูปภาพ แต่การทำให้แอปมีความเสถียรในระยะยาวนั้นมาจากการทำความเข้าใจเสาหลักสำคัญอีก 2 ประการ ได้แก่ การนำกลับมาใช้ใหม่และการรีไซเคิล เซสชันนี้จะนำเสนอ Playbook ที่ใช้งานได้จริงสำหรับการเพิ่มประสิทธิภาพหน่วยความจำโดยใช้เครื่องมือที่ทันสมัย, ไลบรารีโอเพนซอร์สอย่าง Glide และ Coil รวมถึง API ระดับระบบ

ในเซสชันนี้ คุณจะได้เรียนรู้วิธีวิเคราะห์การถดถอยของหน่วยความจำในชีวิตจริงโดยใช้ Android Vitals, ใช้รูปแบบการแคชขั้นสูง (นำกลับมาใช้ใหม่) ด้วย Glide และ Coil และปล่อยหน่วยความจำในเชิงรุก (รีไซเคิล) โดยใช้ Callback onTrimMemory

สรุป

  • Google Play กำลังเปิดตัวเกณฑ์ประสิทธิภาพหน่วยความจำที่จะบังคับใช้ในเดือนกุมภาพันธ์ 2027 การเกินขีดจำกัดเปอร์เซ็นไทล์ที่ 90 จะส่งผลโดยตรงต่อการประเมินประสิทธิภาพของแอปและอาจลดระดับการมองเห็นใน Play Store
  • บิตแมปจะขยายตามขนาดและความลึกของสีเมื่อถอดรหัส ทำให้เป็นตัวใช้หน่วยความจำที่ใหญ่ที่สุดในแอปส่วนใหญ่ รูปภาพความละเอียดเต็มเพียงไม่กี่รูปอาจใช้พื้นที่หลายร้อยเมกะไบต์
  • ลดขนาดรูปภาพและถอดรหัสเฉพาะส่วนที่มองเห็นเพื่อลดการใช้หน่วยความจำ ขอขนาดรูปภาพที่ตรงกันผ่าน CDN หรือใช้ API เช่น BitmapRegionDecoder แทนการโหลดรูปภาพความละเอียดเต็มที่มีขนาดใหญ่เกินไป
  • ตัวโหลดรูปภาพหลายตัวจะเพิ่มการใช้งานแคชฮีปหน่วยความจำเริ่มต้นและทำซ้ำบิตแมปที่จัดเก็บไว้ กำหนดมาตรฐานให้ใช้ Loader เดียวหรือตั้งค่างบประมาณหน่วยความจำที่ชัดเจนใน Loader ที่มีอยู่
  • ปล่อยหน่วยความจำรูปภาพเมื่อแอปย้ายไปทำงานอยู่เบื้องหลังโดยจัดการ onTrimMemory หยุดการโพลและการแสดงภาพเคลื่อนไหวในเบื้องหลังใน onStop เพื่อป้องกันไม่ให้หน่วยความจำเบื้องหลังเพิ่มขึ้น

แหล่งข้อมูล

Gemini Notebook

หัวข้อ

  • bitmap-optimization
  • android-memory
  • android-vitals
  • image-caching
  • memory-profiler

ข้อความถอดเสียง

ข้อมูลเบื้องต้นและภาพรวมหน่วยความจำบิตแมป

  • Fung Lam

    ใช่ เรามีเวลาประมาณ 1 ชั่วโมงก่อนอาหารกลางวัน ดังนั้นโปรดอยู่กับฉันก่อน

  • ผมชื่อ Fung Lam เป็นวิศวกรนักพัฒนาซอฟต์แวร์สัมพันธ์ของ Android ที่ทำงานอยู่ในลอนดอน

  • ได้เลย ขอบคุณนะ

  • ก่อนที่จะเริ่ม ขอถามคำถามได้ไหม

  • มีกี่คนที่ซื้อโทรศัพท์ใหม่ในปีนี้

  • ว้าว

  • ว้าว

  • พูดตรงๆ ได้เลย เราจะไม่ว่าอะไรถ้าคุณไม่ซื้อ Pixel

  • โทรศัพท์รุ่นใหม่มีราคาแพงกว่าใช่ไหม

  • ใช่ นั่นเป็นสาเหตุที่บางคนไม่ซื้อโทรศัพท์ใหม่

  • ทำไมจึงแพงกว่า

  • คุณรู้หรือไม่

  • ไม่

  • RAM

  • RAM ใช่ๆ

  • ใช่ RAM เป็นความท้าทายอย่างมากสำหรับ Android, สำหรับเรา และสำหรับนักพัฒนาแอป

  • วันนี้เราจึงมาบอกวิธีเอาชนะความท้าทายด้านหน่วยความจำหรือ RAM

  • ก่อนหน้านี้ใน Google I/O และ Google I/O Connect เรามีเซสชันเกี่ยวกับหน่วยความจำ ตั้งแต่วิธีที่ระบบปฏิบัติการจัดสรรหน่วยความจำ วิธีโปรไฟล์แอปพลิเคชัน วิธีเพิ่มประสิทธิภาพโค้ดโดยใช้ R8 และแน่นอนว่าตอนนั้นฉันได้พูดถึงบิตแมปไปบ้างแล้ว

  • คุณจะเห็นรูปภาพของฉันในภาพหน้าจอ

  • การเพิ่มประสิทธิภาพหน่วยความจำเป็นหนึ่งในโดเมนขนาดใหญ่ ซึ่งครอบคลุมพื้นที่ต่างๆ เช่น Android Runtime, การเก็บขยะ, เครื่องมือประสิทธิภาพ, การจัดสรรหน่วยความจำออบเจ็กต์, คอมไพเลอร์ ฯลฯ และในเซสชันนี้ ในเวลา 30 นาทีนี้ ผมจะให้ข้อมูลเกี่ยวกับวิธีเพิ่มประสิทธิภาพหน่วยความจำบิตแมป

  • คุณอาจสงสัยว่าทำไมต้องเป็นบิตแมป

  • ทำไมเราจึงควรให้ความสำคัญกับหน่วยความจำบิตแมป

  • เพราะรูปภาพหนึ่งภาพมีค่ามากกว่าคำพูดนับพันคำ

  • นั่นเป็นเรื่องจริง และเราจะอธิบายให้คุณฟังว่าเพราะเหตุใด

  • ในหน้าจอโทรศัพท์ เช่น Pixel 7, Pixel 9 หรือ Pixel 11 Pro ความละเอียดของหน้าจอจะอยู่ที่ประมาณ 2,500 1,200, 1,400

  • ดังนั้น สำหรับแต่ละพิกเซล เราจะใช้ความลึกของสี 32 บิต

  • ดังนั้น สำหรับรูปภาพแบบเต็มหน้าจอ โอ้ ฉันมีนิ้วไม่พอ แต่จะใช้หน่วยความจำประมาณ 15 MB, 50 MB

  • แต่คำในระบบปฏิบัติการคือ 64 บิต ซึ่งหมายความว่ามีขนาด 8 ไบต์

  • ภาพ 1 ภาพมีขนาดเพียง 8 MB เท่านั้น

  • ใช่ 8K

  • ดังนั้น รูปภาพ 1 รูปจึงมีค่ามากกว่าคำพูดนับพันคำอย่างแน่นอน

  • ใช่แล้ว เรื่องนี้สำคัญมาก

  • รูปภาพ 1 รูปมีขนาด 15 MB แล้ว

  • จะเกิดอะไรขึ้นหากแอปของคุณมีรูปภาพ 10 หรือ 100 รูป

  • ก็เป็นจำนวนมากเลยทีเดียว

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

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

  • นี่ไม่ใช่เรื่องของขนาดไฟล์ว่าอยู่ในรูปแบบ JPEG หรือ PNG

  • ไม่ ระบบจะถอดรหัสและใช้หน่วยความจำ

  • ดังนั้น รูปภาพความละเอียดเต็มเพียงไม่กี่รูปก็อาจใช้หน่วยความจำในอุปกรณ์หลายร้อยเมกะไบต์ได้อย่างง่ายดาย

  • และประการที่ 2 เมื่อหน่วยความจำเหลือน้อยมาก ระบบปฏิบัติการจะต้องใช้พลังงานมากขึ้นเพื่อจัดการกับการใช้หน่วยความจำ

  • ซึ่งจะทำให้ UI ดูไม่ราบรื่น

  • หรือระบบปฏิบัติการอาจปิดแอปเพื่อเพิ่มหน่วยความจำด้วย

  • หรือแอปของคุณจะพบข้อผิดพลาดหน่วยความจำไม่เพียงพอ (Out-Of-Memory) และข้อยกเว้นหน่วยความจำไม่เพียงพอ (Out-Of-Memory) ด้วย

  • โชคดีที่ข่าวดีก็คือหน้าปัดนี้มักจะได้รับความนิยมอย่างมากโดยไม่ต้องใช้ความพยายามมากนัก

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

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

  • วันนี้เราจะมาพูดถึง 5 ส่วนง่ายๆ ที่จะช่วยให้แอปของคุณทำงานได้อย่างราบรื่น

  • ก่อนอื่น เราจะมาดูคุณภาพทางเทคนิคของ Google Play ซึ่งเป็นข้อกำหนดด้านคุณภาพทางเทคนิคใหม่ ซึ่งรวมถึงขีดจำกัดหน่วยความจำที่กำลังจะมาถึงและตำแหน่งที่คุณสามารถตรวจสอบขีดจำกัดได้อย่างง่ายดาย

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

  • หลังจากนั้น เราจะแสดงหลักการเพิ่มประสิทธิภาพการใช้งานหน่วยความจำบิตแมปให้คุณดู

  • และสุดท้าย เราจะแชร์เครื่องมือบางอย่างที่อาจช่วยคุณตรวจสอบแอปของคุณเองได้

มาตรฐานคุณภาพและปัญหาที่พบได้ทั่วไปของ Google Play

  • Fung Lam

    ข้อแรกคือข้อกำหนดด้านคุณภาพใหม่ของ Google Play

  • คุณเข้าชม Google Play Console บ่อยเพียงใด

  • ทุกเดือนเลยเหรอ

  • ทุกสัปดาห์ใช่ไหม

  • ไม่เคยเลยใช่ไหม

  • เสียงหัวเราะเหล่านั้นหมายความว่าใช่ไหม

  • หากไม่

  • ไม่

  • คุณได้ไปที่ส่วน Android Vitals ไหม

  • ใช่ ใช่

  • ใช่ คุณรู้เรื่องนี้ก่อนที่ฉันจะสร้างวิดีโอนี้ขึ้นมา

  • ดังนั้น Google Play จึงมีข้อกำหนดด้านคุณภาพใหม่เกี่ยวกับเกณฑ์ประสิทธิภาพ

  • Android และ Play ต้องการให้ผู้ใช้ใช้แอปที่มีคุณภาพดี

  • ดังนั้น เกณฑ์นี้จึงเป็นสิ่งที่เราพยายามนำเสนอสิ่งที่ดีที่สุดให้แก่ผู้ใช้ปลายทาง

  • พูดง่ายๆ ก็คือ "แอปของคุณเมื่อทำงานอยู่เบื้องหลังหรือในแพลตฟอร์มเบื้องหน้าที่สังเกตได้ การใช้งานหน่วยความจำเปอร์เซ็นไทล์ที่ 90 ไม่ควรเกินเกณฑ์นี้

  • หรือมิฉะนั้น แอปของคุณอาจมีการแสดงผลลดลงใน Play Store หรืออื่นๆ"

  • และจะเริ่มบังคับใช้กับแอปและเกมในเดือนกุมภาพันธ์ 2027

  • การเกินเกณฑ์นี้จะส่งผลโดยตรงต่อการประเมินประสิทธิภาพของแอป

  • ดังนั้น อย่าลืมไปที่ Google Play Console แล้วตรวจสอบว่า "เปอร์เซ็นไทล์ที่ 90 ของฉันวันนี้เป็นเท่าไร"

  • และทำการปรับปรุงก่อนถึงกำหนดเวลา

  • นี่คือภาพเคลื่อนไหว

  • "ถ้าคุณไปที่ Google Play Console คุณจะเห็น Android Vitals

  • มีส่วนของความทรงจำ

  • และคุณจะเห็นเปอร์เซ็นไทล์ที่ 90

  • และหากคลิกที่แท็บนี้ คุณจะเห็นค่าเฉลี่ยของการใช้หน่วยความจำเมื่อเวลาผ่านไป

  • นอกจากนี้ยังมีระดับต่างๆ ตาม RAM, รูปแบบของอุปกรณ์, ความหนาแน่นของหน้าจอ, เวอร์ชันระบบปฏิบัติการ ซึ่งจะช่วยให้คุณแยกคอขวดของหน่วยความจำในอุปกรณ์ระดับเริ่มต้นหรือในแอปบางรุ่นได้

  • ตอนนี้คุณอาจจะพูดว่า "ขอบคุณที่บอกว่าแอปของฉันมีขนาดเกินเกณฑ์

  • แต่ฉันไม่รู้จริงๆ ว่าเกิดอะไรขึ้น

  • คุณจะช่วยไหม"

  • ใช่ เราจึงพร้อมช่วยเหลือคุณ

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

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

  • ใช่ วันนี้ฉันก็พบปัญหาที่พบบ่อยบางอย่างเช่นกัน

  • และหวังว่านี่จะเป็นคู่มือการแก้ปัญหาของคุณ

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

  • แต่ตอนนี้ฉันทราบแล้วว่าหัวข้อเหล่านั้นไม่น่าสนใจเท่าไหร่ ดังนั้นวันนี้โปรดอนุญาตให้ฉันเริ่มจากการแก้ปัญหาก่อน แล้วเราจะกลับมาพูดถึงหัวข้อเหล่านั้นในภายหลัง

  • และบอกตามตรงว่านี่คือวิธีที่ฉันอ่านคู่มือทีวีจริงๆ

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

  • ฉันจะเปิดแอป เริ่มแอป เข้าสู่ระบบ ไปที่หน้าต่างๆ เจาะลึก และกลับมา

  • และผมทดสอบแอปใน RAM ที่แตกต่างกัน อุปกรณ์ที่แตกต่างกันด้วย เช่น อุปกรณ์ Android Go ที่มีหน่วยความจำ 2 GB, อุปกรณ์ระดับเริ่มต้นบางรุ่นที่มีหน่วยความจำ 4 GB, อุปกรณ์ระดับกลางบางรุ่นที่มีหน่วยความจำ 4 GB, 6 GB ถึง 8 GB หรืออุปกรณ์แบบพับได้ระดับพรีเมียมบางรุ่นที่มีหน้าจอขนาดใหญ่มาก คุณภาพหน้าจอดีมาก และหน่วยความจำ 16 GB

  • เราวัดการใช้งานหน่วยความจำในอุปกรณ์นี้เมื่ออยู่ในสถานะแอปต่างๆ โดยบันทึกฮีปดัมพ์ และใช้เครื่องมือต่างๆ รวมถึง Perfetto AI Skill เพื่อวิเคราะห์การจัดสรร เพื่อระบุแอนตี้แพทเทิร์นที่พบบ่อย ซึ่งก็คือข้อผิดพลาดที่พบบ่อยที่เราทำในวงกว้าง

  • และนี่คือลักษณะการเปลี่ยนแปลงของหน่วยความจำในสถานะกิจกรรมต่างๆ

  • และจากการวิเคราะห์นี้ เราพบปัญหาที่พบบ่อยกว่า 15 รายการ ตั้งแต่รูปภาพขนาดใหญ่เกินไป ไปจนถึงแคชในเบื้องหลังที่เพิ่มขึ้น

  • นี่คือกรณีศึกษาหลักที่เราจะแชร์และสำรวจในวันนี้

กรณีศึกษา: การปรับขนาดและการถอดรหัสบิตแมป

  • Fung Lam

    ใช่ กรณีศึกษาทั้งหมดเกี่ยวกับเรื่องนี้ที่ฉันแชร์ตอนนี้เป็นเรื่องเกี่ยวกับการวิจัยแอป 500 รายการ

  • ในเมื่อคุณอยู่ที่นี่ เราคิดว่าทุกคนคงชอบ Android ใช่ไหม

  • เราทุกคนรัก Android

  • สมมติว่ามีคนหนึ่งในกลุ่มที่ชอบ Android เหมือนกัน แต่ไม่ได้เป็นผู้เชี่ยวชาญด้านการพัฒนา Android

  • เขาจึงสร้างแอป Android สำหรับสั่งซื้อตุ๊กตา Android

  • แต่มีข้อผิดพลาดบางอย่าง

  • มาพูดคุยกัน มาดูกันว่านี่อาจเป็นข้อผิดพลาดแรกหรือไม่

  • ในหน้าสินค้า เรามีเลย์เอาต์ 2 คอลัมน์ที่แสดงตุ๊กตา Android แบบต่างๆ

  • รูปภาพในแต่ละคอลัมน์มีขนาดประมาณ 400 x 400 พิกเซล

  • แต่จริงๆ แล้วแอปจะดาวน์โหลดรูปภาพ 1 รูปจากเซิร์ฟเวอร์ ไม่ว่าหน้าจอของฉันจะมีขนาดเท่าใดก็ตาม

  • ระบบกำลังดาวน์โหลดรูปภาพขนาด 3000 x 3000

  • ใช่ เพราะนักพัฒนาซอฟต์แวร์จะคิดว่า "Android สวยงามและน่าทึ่งมาก

  • ฉันต้องการแสดงสิ่งที่ดีที่สุดให้ผู้ใช้"

  • แต่จริงๆ แล้วไม่ใช่ เพราะขนาดของมุมมองรูปภาพจะจำกัดไว้

  • ดังนั้น แม้ว่าคุณจะดาวน์โหลดรูปภาพขนาดใหญ่ แต่รูปภาพนั้นก็จะไม่แสดงต่อผู้ใช้อย่างดีที่สุด

  • นอกจากนี้ รูปภาพขนาดใหญ่นี้ยังใช้หน่วยความจำเพิ่มเติมด้วย

  • และแม้ว่าเราจะลดขนาดลง แต่กระบวนการลดขนาดก็ใช้หน่วยความจำเพิ่มเติมด้วย

  • เราควรช่วยนักพัฒนาแอปรายนี้แก้ไขปัญหานี้อย่างไร

  • หากคุณรู้จัก CDN คุณจะทราบว่า CDN สามารถจัดการการดำเนินการบางอย่างเกี่ยวกับรูปภาพได้ เช่น การครอบตัด การปรับขนาด การหมุน การใช้ฟิลเตอร์ การเปลี่ยนรูปภาพจากสี่เหลี่ยมจัตุรัสเป็นวงกลม

  • CDN สามารถจัดการบางอย่างได้ตามคำขอ HTTP

  • ดังนั้น แม้ว่าฝั่งเซิร์ฟเวอร์จะโฮสต์รูปภาพเพียงรูปเดียว คุณก็ใช้ CDN เพื่อส่งรูปภาพที่ปรับขนาดแล้วไปยังอุปกรณ์ได้

  • ดังนั้น ข้อ 1 เมื่อคุณขอรูปภาพ ให้ลองใช้ CDN เพื่อขอรูปภาพเป็นขนาด ImageView

  • แต่หากเป็นไปไม่ได้ เช่น บางครั้งอาจมีขนาดใหญ่กว่า ImageView เล็กน้อย อย่าลืมลดขนาดตัวอย่างเพื่อปรับขนาดในโค้ด

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

  • ตัวอย่างโค้ดสำหรับ Coil มีดังนี้

    // 1. Ask CDN for the exact UI card dimensions (e.g. 540x675)
    val sizedUrl = "$cdnBaseUrl?w=$cardWidthPx&h=$cardHeightPx&fmt=webp"
    
    // 2. Enforce exact downsampling + Hardware Bitmap config
    AsyncImage(
        model = ImageRequest.Builder(LocalContext.current)
            .data(sizedUrl)
            .size(cardWidthPx, cardHeightPx) // Never use Size.ORIGINAL!
            .precision(Precision.EXACT)      // Downsample to exact card size
            .bitmapConfig(Bitmap.Config.HARDWARE)
            .build(),
        contentDescription = product.title
    )
  • คุณสามารถส่งขนาดเป็นข้อความสีเหลืองได้

  • ได้ คุณสามารถส่งขนาดเป็น HTTP ในคำขอ HTTP ได้

  • และในโค้ดที่ไฮไลต์นี้ คุณเห็นไฮไลต์ไหม ใช่ อย่าใช้ Size.ORIGINAL

  • ใช่ ใช้ความกว้างและความสูงเพื่อให้ส่งเป็นพารามิเตอร์

  • ใช้ความแม่นยำเพื่อลดขนาดความละเอียดของรูปภาพให้เป็นขนาดที่แน่นอน

  • และหากเป็นไปได้ ให้ใช้ Bitmap.Config.HARDWARE ซึ่งใช้หน่วยความจำน้อยกว่าด้วย เนื่องจากรูปภาพจะจัดเก็บไว้ใน VRAM โดยตรง

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

  • แต่ยังมีปัญหาอีกอย่างหนึ่งด้วย

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

  • เราจะปรับปรุงให้ดีขึ้นได้อย่างไร

  • ได้

  • เราสามารถตัดรูปภาพให้เล็กลงและแสดงเฉพาะรูปภาพที่แสดงบนหน้าจอที่จะโหลด

  • ส่วนที่อยู่นอกหน้าจอจะไม่โหลดหรือจะยกเลิกการโหลด

  • ดังนั้นจึงใช้หน่วยความจำน้อยลง

  • ในทำนองเดียวกัน ในกรณีนี้ ระบบจะประหยัดหน่วยความจำได้ประมาณ 60%

  • แน่นอนว่าหากคุณตัดแต่งเป็นสี่เหลี่ยมผืนผ้าเล็ก ก็จะประหยัดได้มากขึ้น

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

  • แต่คุณไม่จำเป็นต้องทำเช่นนี้

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

  • ดังนั้น ระบบจะโหลดเฉพาะพื้นที่ที่มองเห็นได้ลงในหน่วยความจำ

  • ฉันจะทำได้อย่างไร

  • อ๋อ มี API ที่ชื่อว่า BitmapRegionDecoder ซึ่งช่วยให้คุณโหลดพื้นที่เฉพาะของรูปภาพได้

  • และแน่นอนว่ายังมีเครื่องมือและไลบรารีบางอย่างที่ช่วยคุณได้

  • คุณค้นหาได้ใน GitHub

  • แอปการ์ตูนหรือแผนที่บางแอปใช้ฟีเจอร์นี้อยู่แล้ว

  • คุณสามารถใช้เป็นข้อมูลอ้างอิงได้

  • นอกจากนี้ เราเป็นมนุษย์ เราย่อมทำผิดพลาดได้

  • AI อาจทำงานผิดพลาดได้เช่นกัน

  • ดังนั้น เพื่อป้องกันไม่ให้เกิดข้อผิดพลาด เราจึงเพิ่มขีดจำกัดเมื่อโหลดรูปภาพได้ด้วย

  • ตัวอย่างเช่น ในกรณีนี้ ฉันอาจพูดว่า "มาใช้ขนาดหน้าต่างหรือขนาดหน้าจอเป็นขีดจำกัด แล้วใส่ไว้ในฟังก์ชัน min, min ของความกว้างที่ขอ หรือความกว้างที่ผูกไว้"

    // Query active window bounds instead of hardcoding 1080 / 1920
    val bounds = WindowMetricsCalculator.getOrCreate()
        .computeCurrentWindowMetrics(activity).bounds
    
    // Clamp target dimensions so Bitmap NEVER exceeds the active window
    val safeWidth  = minOf(requestedWidthPx,  bounds.width())
    val safeHeight = minOf(requestedHeightPx, bounds.height())
    
    val bitmap = Bitmap.createBitmap(safeWidth, safeHeight, config)
  • ดังนั้นเมื่อใช้ฟังก์ชันนี้ ทุกครั้งที่ฉันโหลดรูปภาพ รูปภาพจะไม่เกินขอบเขต

  • ดังนั้น ระบบจะไม่โหลดรูปภาพที่มีขนาดใหญ่กว่าความละเอียดของหน้าจอ

  • โปรดทราบว่าให้ถอดรหัสเฉพาะส่วนที่มองเห็นได้ของรูปภาพเท่านั้น ซึ่งจะช่วยประหยัดหน่วยความจำ

การจัดการหน่วยความจำ รายการที่ซ้ำกัน และแคช

  • Fung Lam

    และขอย้ำอีกครั้งว่าแอปนี้ไม่ได้มาจากนักพัฒนาแอปเพียงรายเดียว

  • จริงๆ แล้วมีไว้สำหรับผู้ร่วมให้ข้อมูลหลายคน และผู้ร่วมให้ข้อมูลอาจใช้เครื่องมือที่แตกต่างกัน

  • เช่น ส่วนหนึ่งของกระบวนการคือการใช้คำว่า "โอ้ ขอโทษ"

  • ขอเริ่มใหม่อีกครั้ง

  • แอปนี้ก็เช่นกัน

  • สวัสดี ฉันอยู่ในหน้าสินค้า ฉันมีรายการสินค้าและเห็นรูปภาพนี้ ฉันจึงเพิ่มลงในรถเข็นช็อปปิ้ง

  • หลังจากนั้นไม่กี่นาที ฉันก็ไปที่หน้าชำระเงิน แล้วรูปภาพเดียวกันก็ปรากฏขึ้นอีกครั้ง

  • แต่รูปภาพนี้เป็นรูปภาพเดียวกันจริงๆ หรือเป็นวัตถุ 2 รายการที่แตกต่างกันในหน่วยความจำ

  • ใช่ ข้อผิดพลาดที่พบบ่อยอย่างหนึ่งที่เราพบคือแอปโหลดรูปภาพจำนวนมาก

  • อย่างไรก็ตาม ผู้ใช้ก็มีรูปภาพที่ซ้ำกันจำนวนมากในหน่วยความจำด้วย

  • หากคุณใช้โปรแกรมโหลดรูปภาพ คุณสามารถส่งคีย์เพื่อทำการแคชได้

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

  • ใน Android Studio คุณสามารถใช้เครื่องมือสร้างโปรไฟล์หน่วยความจำเพื่อดูว่าแอปมีบิตแมปซ้ำกันหรือไม่

  • ฉันได้พูดถึงเรื่องนี้ในเซสชัน Google I/O แล้ว คุณจึงกลับไปดูวิดีโอบน YouTube ได้

  • และหากไม่ได้ใช้ Android Studio ก็ยังมีคำสั่ง adb ให้คุณบันทึกฮีปดัมป์ จากนั้นคุณสามารถใช้ภาษา SQL ของกราฟฮีป Perfetto เพื่อค้นหาว่าแอปมีบิตแมปที่ซ้ำกันหรือไม่

  • นี่คือตัวอย่างโค้ดใน Coil ซึ่งแสดงวิธีเพิ่มคีย์สำหรับการแคชบิตแมป

    // Strip volatile query tokens (?from=list / ?sig=...) for cache key
    val canonicalKey = MemoryCache.Key(productUrl.substringBefore("?"))
    
    // On Checkout Screen: reuse the 200x200 Bitmap already loaded in the List!
    AsyncImage(
        model = ImageRequest.Builder(context)
            .data(productUrl)
            .placeholderMemoryCacheKey(canonicalKey) // Instant zero-decode reuse!
            .memoryCacheKey(canonicalKey)
            .diskCacheKey(productUrl.substringBefore("?"))
            .build(),
        contentDescription = product.title
    )
  • และตอนนี้ก็ใช่เลย

  • ฉันกลับไปที่ตัวอย่างก่อนหน้า

  • สวัสดี แอปของฉันมีนักพัฒนาแอปคนละคนกัน

  • บางส่วนใช้ Views ดังนั้นจึงใช้ Glide เป็นโปรแกรมโหลดรูปภาพ

  • และบางรายใช้ Coil เนื่องจากใช้ Compose

  • คุณเพิ่งพูดถึงเครื่องมือโหลดรูปภาพที่ช่วยเราในการแคช ดังนั้นหากฉันมีเครื่องมือโหลดรูปภาพมากกว่า 1 รายการ จะมีปัญหาอะไรไหม

  • แน่นอนว่ามีบางอย่างผิดปกติ

  • ดังนั้นสำหรับโปรแกรมโหลดรูปภาพ โดยค่าเริ่มต้น โปรแกรมจะใช้ X% ของขนาดหน่วยความจำฮีปเป็นขนาดแคชเริ่มต้น

  • ดังนั้น หากคุณมีโปรแกรมโหลดรูปภาพ 1 รายการ ระบบจะใช้หน่วยความจำ X%

  • หากมีโปรแกรมโหลดรูปภาพ 2 รายการ หน่วยความจำจะเพิ่มขึ้น 2 เท่า

  • หากคุณมีเครื่องมือโหลดรูปภาพ 3 รายการ ก็จะเท่ากับ 3 เท่า

  • และหากโปรแกรมโหลดรูปภาพไม่ได้ใช้พูลเดียวกัน ก็จะเกิดเหตุการณ์เดียวกันนี้

  • เช่น ในหน้าสินค้า ฉันโหลดรูปภาพโดยใช้ Glide จากนั้นในหน้าชำระเงิน ระบบจะโหลดรูปภาพเดียวกันโดยใช้ Coil

  • อย่างไรก็ตาม จะยังมีรูปภาพ 2 รูปในความทรงจำ

  • ไม่ดีเลย

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

  • แต่ถ้างานนี้เป็นงานวิศวกรรมจำนวนมาก ฉันก็ทำไม่ได้

  • วิธีแก้ไขที่ง่ายกว่าคือ ฉันวัดว่าตัวโหลดรูปภาพนี้จะจัดการรูปภาพกี่เปอร์เซ็นต์ ตัวโหลดรูปภาพอื่นจะจัดการรูปภาพกี่เปอร์เซ็นต์ และตั้งค่าน้ำหนักสำหรับตัวโหลดรูปภาพต่างๆ เช่นเดียวกับโค้ดนี้

    val totalBudgetBytes = computeAdaptiveCacheBytes(context)
    val numberOfLoaders = 2 // e.g., Team A (Glide) + Team B (Coil)
    
    // Approach A (With Stats): Weight by image traffic (70% Team A / 30% Team B)
    val loaderABudget = (totalBudgetBytes * 0.70).toInt()
    val loaderBBudget = (totalBudgetBytes * 0.30).toInt()
    
    // Approach B (Simple Fix without stats): Divide by number of loaders!
    val simpleSplitBudget = totalBudgetBytes / numberOfLoaders
    
    MemoryCache.Builder()
        .maxSizeBytes(simpleSplitBudget)
        .build()
  • ฉันสามารถตั้งค่า loaderABudget เป็น 70% และ loaderBBudget เป็น 30% ซึ่งเป็นอีกวิธีหนึ่ง

  • หากคุณไม่ทราบจริงๆ ว่าตัวโหลดแต่ละตัวจะจัดการรูปภาพได้มากน้อยเพียงใด ก็ควรแบ่งรูปภาพออกเป็นส่วนๆ

  • ฉันมีตัวโหลดรูปภาพ 2 ตัว ให้หารด้วย 2 ตัวโหลดรูปภาพ 3 ตัว ให้หารด้วย 3 แทนที่จะใช้ค่าเริ่มต้น เพราะจะทำให้ใช้หน่วยความจำมากเกินไป

  • และสมมติว่าในหน้าสินค้า เราคาดหวังให้ผู้ใช้เลื่อนดูสินค้าเพิ่มเติม

  • โดยปกติแล้ว เราอาจแคชรูปภาพประมาณ 50 รูปหรือ 50 MB

  • แต่จะเกิดอะไรขึ้นหากแอปทำงานอยู่เบื้องหลัง

  • อ๋อ ถ้าแอปทำงานอยู่เบื้องหลัง ผู้ใช้อาจไม่ได้กำลังท่องเว็บอยู่

  • หากผู้ใช้ไม่ได้ท่องเว็บ ฉันควรเก็บรูปภาพแคชจำนวนนี้ไว้ในหน่วยความจำไหม

  • ไม่ เราควรเผยแพร่

  • ใช่ เราควรทำตาม Callback ของ onTrimMemory

  • ใช่ จริงๆ แล้วแอปจำนวนมากในปัจจุบันไม่ได้ใช้หรือไม่ได้คำนึงถึง Callback ของ onTrimMemory

  • สําหรับ Callback ของ onTrimMemory มีสัญญาณ 2 รายการที่เราควรพิจารณา

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

  • และเราอาจคาดหวังว่า "ผู้ใช้ไม่ได้ใช้แอปของคุณ

  • คุณควรปล่อยทรัพยากรให้ระบบปฏิบัติการ"

  • ดังนั้นเราจึงควรตัดขนาดแคช

  • และอีกเหตุผลหนึ่งคือ TRIM_MEMORY_BACKGROUND ซึ่งหมายความว่าแอปทำงานอยู่เบื้องหลังเป็นเวลานานแล้ว และตอนนี้ระบบปฏิบัติการต้องการทรัพยากรบางอย่างจริงๆ

  • ในขั้นตอนนี้ คุณควรปล่อยความทรงจำออกไปให้มากที่สุดเกือบ 100%

  • การทำเช่นนี้จะช่วยลดโอกาสและลดความเสี่ยงที่ระบบปฏิบัติการจะหยุดการทำงานของแอปด้วย

  • เพราะเหมือนกับว่า "เฮ้ พวกเขาต้องการหน่วยความจำ คุณก็ให้พวกเขา แล้วระบบปฏิบัติการนั้นก็จัดการมัน"

  • หากไม่ ระบบปฏิบัติการก็ไม่มีทางเลือกและต้องดำเนินการบางอย่าง

การป้องกันการเพิ่มขึ้นของหน่วยความจำในเบื้องหลัง

  • Fung Lam

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

  • อย่างไรก็ตาม เราพบว่าแอปใช้หน่วยความจำมากขึ้นในเบื้องหลัง

  • ทำไมจึงเป็นเช่นนั้น

  • นั่นคือเหตุผลที่ฉันทำการค้นคว้าข้อมูล

  • ฉันพยายามหาสาเหตุแล้ว

  • และเหตุผลหนึ่งก็คือ "ในแอปของพวกเขามีภาพเคลื่อนไหวหรือภาพสไลด์

  • เช่น ดาวน์โหลดรูปภาพใหม่ทุกๆ 15 วินาที และแสดงรูปภาพใหม่ต่อผู้ใช้"

  • แต่นักพัฒนาแอปไม่ได้หยุดภาพเคลื่อนไหวเมื่อแอปทำงานอยู่เบื้องหลัง

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

  • ดังนั้นนี่จึงเป็นสิ่งที่แอปไม่ควรทำ

  • ใช่แล้ว

  • เราควรหยุดภาพเคลื่อนไหว การแคช หรือการทำงานที่ไม่เกี่ยวข้องทั้งหมดเมื่อแอปกำลังจะทำงานอยู่เบื้องหลัง

  • ดังนั้น หากคุณทราบเกี่ยวกับวงจรของกิจกรรม ซึ่งมีเหตุการณ์ onPause, onStop โปรดตรวจสอบว่าคุณได้ใช้ onStop, onPause, onDestroyView เพื่อหยุดภาพเคลื่อนไหว ใช่ เพื่อหยุดคำขอ Pull Request รูปภาพ หรือสิ่งอื่นๆ ที่เกี่ยวข้องกับงานที่ไม่จำเป็นต้องได้รับความสนใจจากผู้ใช้

    override fun onStop() {
        super.onStop()
        // 1. Stop animations so they don't decode next frames in background!
        bannerAnimationView.cancelAnimation()
        animatedImageDrawable?.stop()
    
        // 2. Cancel next-image buffering / prefetch queues!
        nextImagesBufferingJob?.cancel()
        Glide.with(this).pauseRequests()
    }
    
    override fun onDestroyView() {
        super.onDestroyView()
        bannerAnimationView.clearAnimation() // Release resources!
    }

เฟรมเวิร์ก 3R สำหรับสุขภาพความจำ

  • Fung Lam

    ฉันรู้ว่าฉันได้พูดคุยกับคุณหลายเคสแล้ว แต่ฉันบอกว่า "ฉันมีมากกว่า 15 เคส"

  • ฉันรู้ว่าถ้าฉันพูดแชร์เคสทั้งหมดกับคุณ คุณอาจลืมทุกอย่าง

  • คุณจะกลับไปที่บ้านแล้วค้นหาใน Google ว่า "ฉันจะแก้ไขปัญหาเกี่ยวกับความจำได้อย่างไร"

  • เราจึงทำให้คุณจำได้ง่ายๆ

  • ใช่แล้ว

  • เราจะเชื่อมโยงกับกรอบการทำงาน 3R ที่เป็นที่รู้จักกันดีเพื่อช่วยให้คุณจดจำได้ง่ายขึ้น

  • เฟรมเวิร์ก 3R นี้ เราคิดว่าทุกคนน่าจะเคยได้ยินมาบ้างแล้ว

  • วิธีแรกคือการลด ซึ่งก็คือวิธีลดหน่วยความจำเมื่อโหลดรูปภาพ

  • แน่นอนว่าตามที่ได้กล่าวไปแล้ว เราสามารถลดขนาดความละเอียดของรูปภาพเพื่อให้ได้ขนาดที่เหมาะสมเมื่อได้รับรูปภาพจากเซิร์ฟเวอร์

  • เราเพิ่มประสิทธิภาพความลึกของสี หรือใช้บิตแมปฮาร์ดแวร์หากเป็นไปได้

  • และหากคุณไม่ได้ใช้ CDN ให้ลองใช้ CDN

  • ดังนั้นเมื่อเราดึงรูปภาพจากเซิร์ฟเวอร์ ระบบจะดึงรูปภาพที่พอดีกับขนาดมากที่สุด

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

  • และแน่นอนว่าคุณไม่ควรมีเครื่องมือโหลดรูปภาพหลายรายการในโค้ด เนื่องจากจะทำให้ประสิทธิภาพลดลง

  • ลดการใช้ ใช้ซ้ำ และอะไรอีกอย่างนะ

  • คุณรู้หรือไม่

  • รีไซเคิล

  • ลองใหม่ไหม

  • รีไซเคิล ใช่ รีไซเคิล

  • ใช่ เราเรียนรู้เรื่องการลด การใช้ซ้ำ และการรีไซเคิลในโรงเรียนประถม แม้ว่าเราจะไม่ได้เรียนการเขียนโค้ดในโรงเรียนประถมก็ตาม

  • ใช่ รีไซเคิล ใช่

  • ใช่ เป็นเรื่องเกี่ยวกับวิธีปล่อยหน่วยความจำเพื่อให้แอปพลิเคชันอื่นหรือระบบปฏิบัติการใช้หน่วยความจำได้

  • มีข้อมูลต่างๆ เช่น เกี่ยวกับการรีไซเคิล

  • ก่อนอื่น ฉันได้พูดถึงเหตุการณ์ onTrimMemory ซึ่งมี 2 สัญญาณที่ระบบปฏิบัติการจะส่งให้คุณเมื่อ UI ถูกซ่อนหรือระบบปฏิบัติการต้องการทรัพยากรจริงๆ

  • ได้ ลองจัดการคำขอนี้ดู

  • และแน่นอนว่าหากคุณมีภาพเคลื่อนไหว ตัวโหลดรูปภาพ หรือการทำงานอื่นๆ ที่เกิดขึ้นในเบื้องหน้า ให้หยุดการทำงานดังกล่าวเมื่อเกิดเหตุการณ์ onPause onStop

  • ดังนั้นให้ลดการใช้ นำกลับมาใช้ซ้ำ และรีไซเคิล

  • จำง่ายใช่ไหม

ชุดเครื่องมือวินิจฉัยสำหรับนักพัฒนาแอปและการวิเคราะห์ AI

  • Fung Lam

    ตอนนี้เราจะกลับไปที่ส่วนที่น่าสนใจน้อยกว่าของคู่มือทีวี

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

  • เครื่องมือแรกคือ Google Play Console, Android Vitals และ Reporting API

  • ได้เลย ให้ตรวจสอบเปอร์เซ็นไทล์ที่ 90 ใน Google Play Console

  • ตอนนี้คุณอยู่ที่ไหน

  • หรือใช้ Reporting API หากคุณไม่ได้เข้าชมหน้าเว็บ

  • คุณยังคงใช้ API เพื่อรับค่าได้

  • ใช่ ตรวจสอบให้แน่ใจว่าเกณฑ์คือ 200 MB และ 400 MB

  • ตรวจสอบว่าคุณไม่ได้ใช้เกินเกณฑ์

  • นอกจากนี้ยังมีคำสั่ง adb ที่ช่วยคุณได้ เช่น การบันทึกฮีปดัมป์ หรือการส่งเหตุการณ์เพื่อจำลองว่าแอปอยู่ในระยะUI_HIDDENแล้ว

    adb shell dumpsys meminfo <pkg>  # watch Native Heap!
    adb shell am send-trim-memory <pkg> UI_HIDDEN
  • หากคุณใช้ Android Studio อยู่แล้วก็ดีเลย

  • แต่ก็ยังเป็นเพื่อนที่ดีสำหรับคุณเสมอ

  • คุณสามารถใช้เครื่องมือสร้างโปรไฟล์ของ Android Studio เพื่อบันทึกฮีปดัมป์ เพื่อให้มี UI สำหรับดูการใช้งานหน่วยความจำและการจัดสรรหน่วยความจำจริง

  • และแน่นอนว่าเราจะพูดถึง AI ด้วย Gemini ใน Android Studio หรือทักษะอื่นๆ ของ Android ที่มีให้ใช้งาน

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

  • รูปภาพของฉันโหลดโดย Glide หรือโหลดโดย Coil

  • มีหน่วยความจำรั่วไหลอื่นๆ ในแอปพลิเคชันของฉันไหม

  • เราทราบดีว่าบางคนอาจบอกว่า "จริงๆ แล้วฉันไม่ได้ใช้ IDE อีกต่อไปแล้ว

  • ฉันเป็นวิศวกรพรอมต์

  • ฉันใช้ ใช่ ฉันแค่ส่งพรอมต์ด้วย AI Agent เพื่อเขียนโค้ด"

  • ใช่ ฉันรู้

  • นอกจากนี้ ฉันยังเขียนโค้ดแบบ Vibe Coding ในเวลาว่างด้วย

  • เราจึงจะแชร์ตัวอย่างให้คุณดู

  • ใช่ นี่เป็นเพียงส่วนหนึ่งของพรอมต์ที่ฉันใช้สำหรับการวิจัยแอป 500 รายการ

  • ใช่ แค่บางส่วนเท่านั้น

  • คุณก็รู้ว่าหน้าจอไม่ได้ใหญ่มาก

  • ถ้าใส่มากกว่านี้ คุณจะแทบมองไม่เห็นเลย

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

  • และใช่ ฉันสามารถพูดที่นี่ได้อีกประโยคหนึ่งว่า "โปรดระบุโค้ดที่แก้ไขแล้วสำหรับโค้ดของฉันด้วย"

  • ใช่ ฉันหวังว่าข้อมูลนี้จะช่วยลดความกลัวของคุณได้ เช่น "เฮ้ ความทรงจำเป็นโดเมนขนาดใหญ่

  • ฉันไม่ได้เรียนเรื่องระบบปฏิบัติการหรือสถาปัตยกรรมระบบคอมพิวเตอร์ในสาขาวิทยาการคอมพิวเตอร์ที่มหาวิทยาลัย

  • แต่จริงๆ แล้วเรื่องนี้ไม่ได้น่ากลัวหรือยากขนาดนั้น"

  • ใช่ วิธีนี้ช่วยให้คุณวิเคราะห์แอปได้ง่ายๆ

  • โปรดลองใช้

  • แต่แน่นอนว่า AI จะทำงานผิดพลาด ดังนั้นความรู้พื้นฐานเกี่ยวกับวิธีการใช้งานหน่วยความจำและวิธีที่ระบบปฏิบัติการจัดสรรหน่วยความจำจึงยังคงมีความสำคัญ

  • ดังนั้นโปรดมาฟังการบรรยายของฉันและดูวิดีโอของเราบน YouTube

สรุปประเด็นสำคัญ

  • Fung Lam

    สรุปก็คือ เรามีประเด็นสำคัญบางอย่าง

  • ขั้นแรกคือลด

  • ตรวจสอบว่าคุณใช้รูปภาพที่มีขนาดถูกต้อง

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

  • ใช้bitmapConfigหรือRGB_565ฮาร์ดแวร์ทุกครั้งที่เป็นไปได้

  • นำกลับมาใช้ใหม่ ดังนั้นโปรดตรวจสอบว่าคุณมีกลยุทธ์การแคช และใช้โปรแกรมโหลดรูปภาพเพื่อช่วยลดจำนวนบิตแมปที่ซ้ำกันในหน่วยความจำ

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

  • และเราจะไม่นำกลับมาใช้ซ้ำหรือรีไซเคิล

  • ใช่แล้ว

  • การจัดการหน่วยความจำของแอปควรปรับตามสถานะของแอป

  • หากซ่อน UI ไว้ คุณควรใช้หน่วยความจำน้อยลง

  • หากระบบปฏิบัติการขอให้คุณปล่อยหน่วยความจำจริงๆ ให้ปล่อยหน่วยความจำให้มากที่สุดเท่าที่จะทำได้

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

  • ใช่ หยุดตัวจับเวลาแบนเนอร์ หยุดภาพเคลื่อนไหว หยุดการรวมรูปภาพ

  • และสุดท้าย อย่าลืมตรวจสอบ

  • ไปที่ Google Play Console เป็นประจำ

  • โปรดตรวจสอบว่านี่เป็นการฝึกฝน

  • ใช่ ข้อมูลนี้จะช่วยให้คุณเข้าใจว่าสถานะปัจจุบันของเปอร์เซ็นไทล์ที่ 90 ของการใช้งานหน่วยความจำของแอปในประชากรผู้ใช้ทั้งหมดเป็นอย่างไร

  • ซึ่งจะช่วยให้คุณระบุปัญหาได้ตั้งแต่เนิ่นๆ ด้วย

  • ใช่แล้ว

  • หากต้องการสร้างโปรไฟล์ในเครื่อง คุณสามารถใช้เครื่องมือสร้างโปรไฟล์หน่วยความจำของ Android Studio ได้

  • เรายังมีคำสั่ง ADB ด้วย

  • นอกจากนี้ เรายังมีทักษะเกี่ยวกับ Android ด้วย

  • แต่หากต้องการดูสถิติสำหรับผู้ใช้จริง Google Play Console คือที่ที่เหมาะสำหรับคุณ

  • และยังมี Google Play Reporting API ให้คุณรับข้อมูลโดยใช้ API โดยไม่ต้องเข้าสู่ระบบ Play Console ด้วย

  • และเช่นเคย หากต้องการดูข้อมูลเพิ่มเติม โปรดไปที่ developer.android.com/memory

  • ลด ใช้ซ้ำ และรีไซเคิลด้วยสิ่งนี้

  • เราสามารถสร้างระบบนิเวศของ Android ที่ดีขึ้นได้ คุณสามารถ ฉันหวังว่าเราจะร่วมกันสร้างระบบนิเวศของ Android ที่ดีขึ้นเพื่อเอาชนะความท้าทายที่เรากำลังเผชิญอยู่ได้

  • ขอขอบคุณและขอให้สนุกกับการเขียนโค้ด

  • ฉันจะจัดช่วงเวลาให้คำปรึกษาที่ด้านนอกในเร็วๆ นี้ หากมีคำถามก็มาหาฉันได้เลย

  • และวิทยากรคนถัดไปก็มีเวลาอยู่บ้าง ดังนั้นโปรดรอวิทยากรคนถัดไปขึ้นเวที และขอขอบคุณเป็นอย่างยิ่ง

รหัส

ข้อมูลโค้ด

ข้อมูลโค้ด Kotlin ที่นาที 11:50 ใน "กรณีศึกษา: การปรับขนาดและการถอดรหัสบิตแมป"
// 1. Ask CDN for the exact UI card dimensions (e.g. 540x675)
val sizedUrl = "$cdnBaseUrl?w=$cardWidthPx&h=$cardHeightPx&fmt=webp"

// 2. Enforce exact downsampling + Hardware Bitmap config
AsyncImage(
    model = ImageRequest.Builder(LocalContext.current)
        .data(sizedUrl)
        .size(cardWidthPx, cardHeightPx) // Never use Size.ORIGINAL!
        .precision(Precision.EXACT)      // Downsample to exact card size
        .bitmapConfig(Bitmap.Config.HARDWARE)
        .build(),
    contentDescription = product.title
)
ข้อมูลโค้ด Kotlin ที่นาที 14:45 ใน "กรณีศึกษา: การปรับขนาดและการถอดรหัสบิตแมป"
// Query active window bounds instead of hardcoding 1080 / 1920
val bounds = WindowMetricsCalculator.getOrCreate()
    .computeCurrentWindowMetrics(activity).bounds

// Clamp target dimensions so Bitmap NEVER exceeds the active window
val safeWidth  = minOf(requestedWidthPx,  bounds.width())
val safeHeight = minOf(requestedHeightPx, bounds.height())

val bitmap = Bitmap.createBitmap(safeWidth, safeHeight, config)
ข้อมูลโค้ด Kotlin ที่เวลา 16:51 ใน "การจัดการหน่วยความจำ ข้อมูลที่ซ้ำกัน และแคช"
// Strip volatile query tokens (?from=list / ?sig=...) for cache key
val canonicalKey = MemoryCache.Key(productUrl.substringBefore("?"))

// On Checkout Screen: reuse the 200x200 Bitmap already loaded in the List!
AsyncImage(
    model = ImageRequest.Builder(context)
        .data(productUrl)
        .placeholderMemoryCacheKey(canonicalKey) // Instant zero-decode reuse!
        .memoryCacheKey(canonicalKey)
        .diskCacheKey(productUrl.substringBefore("?"))
        .build(),
    contentDescription = product.title
)
ข้อมูลโค้ด Kotlin ที่ 18:17 ใน "การจัดการหน่วยความจำ รายการที่ซ้ำกัน และแคช"
val totalBudgetBytes = computeAdaptiveCacheBytes(context)
val numberOfLoaders = 2 // e.g., Team A (Glide) + Team B (Coil)

// Approach A (With Stats): Weight by image traffic (70% Team A / 30% Team B)
val loaderABudget = (totalBudgetBytes * 0.70).toInt()
val loaderBBudget = (totalBudgetBytes * 0.30).toInt()

// Approach B (Simple Fix without stats): Divide by number of loaders!
val simpleSplitBudget = totalBudgetBytes / numberOfLoaders

MemoryCache.Builder()
    .maxSizeBytes(simpleSplitBudget)
    .build()
ข้อมูลโค้ด Kotlin ที่ 22:05 ใน "การป้องกันการเพิ่มขึ้นของหน่วยความจำในเบื้องหลัง"
override fun onStop() {
    super.onStop()
    // 1. Stop animations so they don't decode next frames in background!
    bannerAnimationView.cancelAnimation()
    animatedImageDrawable?.stop()

    // 2. Cancel next-image buffering / prefetch queues!
    nextImagesBufferingJob?.cancel()
    Glide.with(this).pauseRequests()
}

override fun onDestroyView() {
    super.onDestroyView()
    bannerAnimationView.clearAnimation() // Release resources!
}
ตัวอย่าง Shell ที่ 25:17 ใน "Developer diagnostic toolkit & AI analysis"
adb shell dumpsys meminfo <pkg>  # watch Native Heap!
adb shell am send-trim-memory <pkg> UI_HIDDEN