การทำงานกับรูปภาพอาจทำให้เกิดปัญหาด้านประสิทธิภาพได้อย่างรวดเร็วหากคุณไม่ระมัดระวัง แม้แต่กราฟิกขนาดเล็กในรูปแบบที่บีบอัด เช่น JPG หรือ PNG ก็อาจกลายเป็นบิตแมปขนาดใหญ่เมื่อถอดรหัสเพื่อแสดง หากคุณใช้กราฟิกอย่างไม่เหมาะสม อาจพบปัญหาเกี่ยวกับหน่วยความจำ ซึ่งอาจส่งผลต่อประสิทธิภาพของแอปและแอปอื่นๆ ในอุปกรณ์ ทำตามแนวทางปฏิบัติแนะนำต่อไปนี้เพื่อให้มั่นใจว่าแอปจะทำงานได้อย่างเต็มประสิทธิภาพ
ใช้ไลบรารีการโหลดรูปภาพ
คุณสามารถปรับปรุงประสิทธิภาพของแอปได้โดยใช้ไลบรารีการโหลดรูปภาพ เช่น Coil (สำหรับโปรเจ็กต์ Kotlin-first) หรือ Glide (สำหรับโปรเจ็กต์ Java) ไลบรารีเหล่านี้ช่วยลดการใช้งานหน่วยความจำของแอปด้วยการดำเนินการต่างๆ เช่น การแคชรูปภาพ การลดขนาดความละเอียดของกราฟิกเมื่อจำเป็น และการรีไซเคิลออบเจ็กต์กราฟิก
ลดขนาดตัวอย่างรูปภาพ
โปรดใช้ขนาดรูปภาพที่เหมาะสมกับความต้องการของคุณ คุณควรหลีกเลี่ยง การโหลดรูปภาพขนาดใหญ่ความละเอียดสูงลงในคอนเทนเนอร์ขนาดเล็ก (เช่น ภาพปก) แต่ให้ใช้การลดขนาดความละเอียดของรูปภาพเพื่อปรับขนาดรูปภาพลงก่อน ถอดรหัสลงในหน่วยความจำแทน
การดาวน์แซมปลิงฝั่งไคลเอ็นต์
ไลบรารีการโหลดรูปภาพ เช่น Coil และ Glide จะจัดการการลดขนาดความละเอียดของรูปภาพให้คุณโดยอัตโนมัติ
คุณกำหนดค่ากลยุทธ์การสุ่มตัวอย่างลงได้
โดยใช้ ImageLoader (สำหรับ Coil) หรือ DownsampleStrategy
(สำหรับ Glide) หากจัดการบิตแมปด้วยตนเอง คุณสามารถใช้
inSampleSize เพื่อถอดรหัสเวอร์ชันที่เล็กลงได้ หากต้องการดำเนินการนี้อย่างปลอดภัย คุณควร
ขั้นแรกตั้งค่า inJustDecodeBounds เป็น true เพื่ออ่านขนาดรูปภาพ
โดยไม่ต้องจัดสรรหน่วยความจำ คำนวณขนาดตัวอย่าง ตั้งค่า inSampleSize เป็น
ค่านั้น ตั้งค่า inJustDecodeBounds เป็น false แล้วถอดรหัสรูปภาพ
เลือกใช้การปรับขนาดฝั่งเซิร์ฟเวอร์
หากเป็นไปได้ ให้ขอขนาดรูปภาพที่แน่นอนที่คุณต้องการจากเซิร์ฟเวอร์แบ็กเอนด์โดยตรง ซึ่งจะช่วยลดการใช้ระบบเครือข่ายและร่องรอยแคชดิสก์ ขณะเดียวกันก็ช่วยให้การใช้งานหน่วยความจำน้อยลงด้วยการหลีกเลี่ยงค่าใช้จ่ายในการใช้หน่วยความจำของการปรับขนาดรูปภาพในอุปกรณ์
คุณกำหนดค่าไลบรารีให้ต่อท้ายขนาดมุมมองเป้าหมายแบบไดนามิกลงใน
URL ของรูปภาพได้ ตัวอย่างเช่น Coil อนุญาตให้ใช้สิ่งนี้โดยใช้ตัวสกัดกั้นที่กำหนดเอง และ Glide
รองรับการใช้ตัวโหลดโมเดลที่กำหนดเอง (เช่น BaseGlideUrlLoader)
หลีกเลี่ยงขนาดเลย์เอาต์ที่ไม่จำกัด
โปรแกรมโหลดรูปภาพต้องทราบขนาดเป้าหมายก่อนที่จะดำเนินการตามคำขอ เพื่อให้ลดขนาดตัวอย่าง (ฝั่งไคลเอ็นต์หรือฝั่งเซิร์ฟเวอร์) ได้อย่างมีประสิทธิภาพ
หลีกเลี่ยงการใช้ wrapContentSize หรือปล่อยให้ขนาดไม่จำกัดใน
Composables ที่โหลดรูปภาพจากระยะไกล หากไลบรารีเหล่านี้ไม่สามารถอนุมานขอบเขตเป้าหมายได้ ระบบจะกลับไปโหลดรูปภาพต้นฉบับแบบเต็มขนาด
ซึ่งอาจทำให้โหลดรูปภาพที่มีขนาดใหญ่กว่าที่จำเป็นมาก ส่งผลให้การใช้งานหน่วยความจำและเวลาในการตอบสนองเพิ่มขึ้น
แต่ให้กำหนดขนาดที่ชัดเจนในรูปภาพที่ใช้ร่วมกันได้ (เช่น ใช้
Modifier.size) หรือกำหนดสัดส่วนภาพแทน ซึ่งช่วยให้เครื่องมือเลย์เอาต์คำนวณเป้าหมายพิกเซลที่แน่นอนล่วงหน้าได้ จากนั้นตัวโหลดรูปภาพจะใช้เป้าหมายนี้เพื่อขอและถอดรหัสชิ้นงานที่มีขนาดถูกต้อง
ระบุแหล่งข้อมูลสำรองสำหรับขนาดหน้าจอต่างๆ
หากคุณจัดส่งรูปภาพพร้อมกับแอป ให้พิจารณาจัดหาชิ้นงานที่มีขนาดแตกต่างกัน สำหรับความละเอียดของอุปกรณ์ที่แตกต่างกัน ซึ่งจะช่วยลดขนาดการดาวน์โหลด ของแอปในอุปกรณ์ และปรับปรุงประสิทธิภาพเนื่องจากจะโหลดรูปภาพที่มีความละเอียดต่ำกว่า ในอุปกรณ์ที่มีความละเอียดต่ำกว่า ดูข้อมูลเพิ่มเติมเกี่ยวกับการระบุบิตแมปสำรองสำหรับขนาดอุปกรณ์ต่างๆ ได้ที่เอกสารประกอบเกี่ยวกับบิตแมปสำรอง
อย่าใช้ระยะห่างโดยตรง
บางครั้งคุณอาจต้องเพิ่มระยะขอบให้กับรูปภาพ เช่น คุณอาจ
ต้องการให้รูปภาพมีเส้นขอบโปร่งใสเพื่อใช้ Letterbox
ในกรณีดังกล่าว อย่าเพิ่มระยะขอบลงในรูปภาพโดยตรงเพื่อเปลี่ยนขนาดของรูปภาพ
แต่ให้คงขนาดของรูปภาพไว้ตามเดิม
และปรับตำแหน่งของรูปภาพบนหน้าจอโดยใช้ InsetDrawable
หรือจะเพิ่มระยะห่างภายในลงใน Composable หรือ View ที่มีรูปภาพก็ได้
เลือกรูปแบบพิกเซลที่เหมาะสม
ปรับสมดุลหน่วยความจำและคุณภาพโดยเลือกรูปแบบพิกเซลที่เหมาะสม ใช้ RGB_565
เมื่อไม่ต้องการความโปร่งใส รูปแบบนี้ใช้หน่วยความจำครึ่งหนึ่งของรูปแบบ ARGB_8888 เริ่มต้น
ใน Glide คุณกำหนดค่านี้ได้โดยใช้ DecodeFormat ใน Coil คุณสามารถ
ใช้พร็อพเพอร์ตี้ bitmapConfig ได้
ใช้เวกเตอร์หากทำได้
สำหรับรูปภาพที่ประกอบด้วยรูปทรงเรขาคณิต ภาพกราฟิกเวกเตอร์จะมีขนาดเล็กกว่าบิตแมปมาก และปรับขนาดได้อย่างราบรื่นสำหรับความหนาแน่นในการแสดงผลทุกระดับ เมื่อเหมาะสม ให้ใช้องค์ประกอบ
เช่น ShapeDrawable เพื่อแสดงกราฟิก
เผยแพร่และนำบิตแมปกลับมาใช้ใหม่เมื่อทำได้
ไฟล์กราฟิกขนาดใหญ่อาจใช้หน่วยความจำมาก คุณควรปล่อยหรือนำออบเจ็กต์กราฟิกกลับมาใช้ใหม่ทุกครั้งที่ทำได้เพื่อลดผลกระทบ
หากใช้ไลบรารีการโหลดรูปภาพ โปรดตรวจสอบว่าได้ปล่อย บิตแมปไปยังพูลที่มีการจัดการของไลบรารีเมื่อไม่ต้องการใช้แล้ว ไลบรารีจะนำออบเจ็กต์กลับมาใช้ใหม่ได้เมื่อจำเป็น และจะเก็บ บัฟเฟอร์หน่วยความจำไว้สำหรับความต้องการในอนาคต
หากจัดการกราฟิกด้วยตนเอง คุณควรปล่อยบิตแมปเมื่อใช้งานเสร็จแล้วโดยเรียกใช้ Bitmap.recycle และทิ้งการอ้างอิง Bitmap ทันที แทนที่จะอาศัยระบบจัดการหน่วยความจำที่ไม่ใช้แล้ว
เคล็ดลับและคำแนะนำอื่นๆ
ส่วนนี้แสดงวิธีอื่นๆ บางส่วนในการปรับปรุงประสิทธิภาพของแอปเมื่อ จัดการกราฟิก
อย่าแพ็กเกจรูปภาพขนาดใหญ่กับไฟล์ AAB/APK
สาเหตุหลักประการหนึ่งที่ทำให้ขนาดการดาวน์โหลดแอปมีขนาดใหญ่คือ กราฟิกที่ แพ็กเกจภายในไฟล์ AAB หรือ APK ใช้เครื่องมือ APK Analyzer เพื่อให้แน่ใจว่า คุณไม่ได้แพ็กเกจไฟล์รูปภาพที่มีขนาดใหญ่กว่าที่จำเป็น ลดขนาดหรือ พิจารณาจัดเก็บรูปภาพไว้ในเซิร์ฟเวอร์และดาวน์โหลดเฉพาะเมื่อจำเป็น
ค้นหาบิตแมปที่ซ้ำซ้อน
หากมีรูปภาพเดียวกันหลายสำเนา จะทำให้สิ้นเปลืองหน่วยความจำ คุณสามารถใช้เครื่องมือสร้างโปรไฟล์ของ Android Studio เพื่อระบุกราฟิกที่ซ้ำซ้อน ใช้เครื่องมือวิเคราะห์ฮีปดัมป์เพื่อบันทึกฮีปดัมป์ และกรองผลลัพธ์โดยเลือกการตั้งค่าบิตแมปที่ซ้ำกัน
เมื่อใช้ ImageBitmap ให้โทรหา prepareToDraw ก่อนวาด
เมื่อใช้ ImageBitmap ให้เรียกใช้ ImageBitmap#prepareToDraw() ก่อนที่จะวาดจริงเพื่อเริ่มกระบวนการอัปโหลดพื้นผิวไปยัง GPU ซึ่งจะช่วยให้ GPU เตรียมพื้นผิวและปรับปรุงประสิทธิภาพในการแสดงภาพบนหน้าจอ ไลบรารีการโหลดรูปภาพส่วนใหญ่จะทำการเพิ่มประสิทธิภาพนี้อยู่แล้ว แต่หากคุณทำงานกับคลาส ImageBitmap ด้วยตนเอง ก็ควรคำนึงถึงเรื่องนี้ด้วย
แนะนำให้ส่ง Int DrawableRes หรือ URL เป็นพารามิเตอร์ไปยัง Composable แทน Painter
เนื่องจากความซับซ้อนของการจัดการรูปภาพ (เช่น การเขียนฟังก์ชันเท่ากับ
สำหรับ Bitmaps จะมีค่าใช้จ่ายในการคำนวณสูง) เราจึงไม่ได้ทำเครื่องหมาย API Painter ว่าเป็น API ที่เสถียรอย่างชัดเจนด้วยคำอธิบายประกอบ @Stable
คลาสที่ไม่เสถียรอาจทำให้เกิดการจัดองค์ประกอบใหม่โดยไม่จำเป็นเนื่องจากคอมไพเลอร์ไม่สามารถอนุมานได้ง่ายๆ ว่าข้อมูลมีการเปลี่ยนแปลงหรือไม่
ดังนั้น เราขอแนะนำให้ส่ง URL หรือรหัสทรัพยากรที่วาดได้เป็นพารามิเตอร์
ไปยัง Composable แทนการส่ง Painter เป็นพารามิเตอร์
// Prefer this:
@Composable
fun MyImage(url: String) {
}
// Over this:
@Composable
fun MyImage(painter: Painter) {
}
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- ImageBitmap เทียบกับ ImageVector {:#bitmap-vs-vector}
- บันทึกสถานะ UI ใน Compose
- ระยะของ Jetpack Compose