การจัดการหน่วยความจำบิตแมปมีความสำคัญอย่างยิ่งต่อการลดความเสี่ยงของหน่วยความจำไม่เพียงพอ (OOM) ในแอป Android ที่มีภาพจำนวนมาก แม้ว่านักพัฒนาแอปจะทราบดีว่าต้องลดขนาดรูปภาพ แต่การทำให้แอปมีความเสถียรในระยะยาวนั้นมาจากการทำความเข้าใจเสาหลักสำคัญอีก 2 ประการ ได้แก่ การนำกลับมาใช้ใหม่และการรีไซเคิล เซสชันนี้จะนำเสนอ Playbook ที่ใช้งานได้จริงสำหรับการเพิ่มประสิทธิภาพหน่วยความจำโดยใช้เครื่องมือที่ทันสมัย, ไลบรารีโอเพนซอร์สอย่าง Glide และ Coil รวมถึง API ระดับระบบ
ในเซสชันนี้ คุณจะได้เรียนรู้วิธีวิเคราะห์การถดถอยของหน่วยความจำในชีวิตจริงโดยใช้ Android Vitals, ใช้รูปแบบการแคชขั้นสูง (นำกลับมาใช้ใหม่) ด้วย Glide และ Coil และปล่อยหน่วยความจำในเชิงรุก (รีไซเคิล) โดยใช้ Callback onTrimMemory
BitmapRegionDecoder แทนการโหลดรูปภาพความละเอียดเต็มที่มีขนาดใหญ่เกินไปonTrimMemory หยุดการโพลและการแสดงภาพเคลื่อนไหวในเบื้องหลังใน onStop เพื่อป้องกันไม่ให้หน่วยความจำเบื้องหลังเพิ่มขึ้นข้อความถอดเสียง
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 รายการ วิธีที่แอปพลิเคชันเหล่านั้นใช้หน่วยความจำ แอปพลิเคชันเหล่านั้นใช้หน่วยความจำอย่างถูกต้องหรือไม่ หรือมีสิ่งใดที่เราเรียนรู้ได้หรือไม่
หลังจากนั้น เราจะแสดงหลักการเพิ่มประสิทธิภาพการใช้งานหน่วยความจำบิตแมปให้คุณดู
และสุดท้าย เราจะแชร์เครื่องมือบางอย่างที่อาจช่วยคุณตรวจสอบแอปของคุณเองได้
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!
}Fung Lam
ฉันรู้ว่าฉันได้พูดคุยกับคุณหลายเคสแล้ว แต่ฉันบอกว่า "ฉันมีมากกว่า 15 เคส"
ฉันรู้ว่าถ้าฉันพูดแชร์เคสทั้งหมดกับคุณ คุณอาจลืมทุกอย่าง
คุณจะกลับไปที่บ้านแล้วค้นหาใน Google ว่า "ฉันจะแก้ไขปัญหาเกี่ยวกับความจำได้อย่างไร"
เราจึงทำให้คุณจำได้ง่ายๆ
ใช่แล้ว
เราจะเชื่อมโยงกับกรอบการทำงาน 3R ที่เป็นที่รู้จักกันดีเพื่อช่วยให้คุณจดจำได้ง่ายขึ้น
เฟรมเวิร์ก 3R นี้ เราคิดว่าทุกคนน่าจะเคยได้ยินมาบ้างแล้ว
วิธีแรกคือการลด ซึ่งก็คือวิธีลดหน่วยความจำเมื่อโหลดรูปภาพ
แน่นอนว่าตามที่ได้กล่าวไปแล้ว เราสามารถลดขนาดความละเอียดของรูปภาพเพื่อให้ได้ขนาดที่เหมาะสมเมื่อได้รับรูปภาพจากเซิร์ฟเวอร์
เราเพิ่มประสิทธิภาพความลึกของสี หรือใช้บิตแมปฮาร์ดแวร์หากเป็นไปได้
และหากคุณไม่ได้ใช้ CDN ให้ลองใช้ CDN
ดังนั้นเมื่อเราดึงรูปภาพจากเซิร์ฟเวอร์ ระบบจะดึงรูปภาพที่พอดีกับขนาดมากที่สุด
นำมาใช้ซ้ำ เราจึงใช้แคชเพื่อให้ระบบนำรูปภาพที่โหลดลงในหน่วยความจำมาใช้ซ้ำ และเราจะไม่มีรูปภาพบิตแมปที่ซ้ำกันในหน่วยความจำ
และแน่นอนว่าคุณไม่ควรมีเครื่องมือโหลดรูปภาพหลายรายการในโค้ด เนื่องจากจะทำให้ประสิทธิภาพลดลง
ลดการใช้ ใช้ซ้ำ และอะไรอีกอย่างนะ
คุณรู้หรือไม่
รีไซเคิล
ลองใหม่ไหม
รีไซเคิล ใช่ รีไซเคิล
ใช่ เราเรียนรู้เรื่องการลด การใช้ซ้ำ และการรีไซเคิลในโรงเรียนประถม แม้ว่าเราจะไม่ได้เรียนการเขียนโค้ดในโรงเรียนประถมก็ตาม
ใช่ รีไซเคิล ใช่
ใช่ เป็นเรื่องเกี่ยวกับวิธีปล่อยหน่วยความจำเพื่อให้แอปพลิเคชันอื่นหรือระบบปฏิบัติการใช้หน่วยความจำได้
มีข้อมูลต่างๆ เช่น เกี่ยวกับการรีไซเคิล
ก่อนอื่น ฉันได้พูดถึงเหตุการณ์ onTrimMemory ซึ่งมี 2 สัญญาณที่ระบบปฏิบัติการจะส่งให้คุณเมื่อ UI ถูกซ่อนหรือระบบปฏิบัติการต้องการทรัพยากรจริงๆ
ได้ ลองจัดการคำขอนี้ดู
และแน่นอนว่าหากคุณมีภาพเคลื่อนไหว ตัวโหลดรูปภาพ หรือการทำงานอื่นๆ ที่เกิดขึ้นในเบื้องหน้า ให้หยุดการทำงานดังกล่าวเมื่อเกิดเหตุการณ์ onPause onStop
ดังนั้นให้ลดการใช้ นำกลับมาใช้ซ้ำ และรีไซเคิล
จำง่ายใช่ไหม
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 ที่ดีขึ้นเพื่อเอาชนะความท้าทายที่เรากำลังเผชิญอยู่ได้
ขอขอบคุณและขอให้สนุกกับการเขียนโค้ด
ฉันจะจัดช่วงเวลาให้คำปรึกษาที่ด้านนอกในเร็วๆ นี้ หากมีคำถามก็มาหาฉันได้เลย
และวิทยากรคนถัดไปก็มีเวลาอยู่บ้าง ดังนั้นโปรดรอวิทยากรคนถัดไปขึ้นเวที และขอขอบคุณเป็นอย่างยิ่ง
// 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
)// 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)// 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
)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()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!
}adb shell dumpsys meminfo <pkg> # watch Native Heap!
adb shell am send-trim-memory <pkg> UI_HIDDEN