การทำงานกับรูปภาพอาจทำให้เกิดปัญหาด้านประสิทธิภาพได้อย่างรวดเร็วหากคุณไม่ระมัดระวัง แม้แต่กราฟิกขนาดเล็กในรูปแบบที่บีบอัด เช่น JPG หรือ PNG ก็อาจกลายเป็นบิตแมปขนาดใหญ่ได้เมื่อถอดรหัสเพื่อแสดงผล หากใช้กราฟิกอย่างไม่มีประสิทธิภาพ คุณอาจพบปัญหาเกี่ยวกับหน่วยความจำซึ่งอาจส่งผลเสียต่อประสิทธิภาพของแอปและแอปอื่นๆ ในอุปกรณ์ ทำตามแนวทางปฏิบัติแนะนำต่อไปนี้เพื่อให้แอปทำงานได้อย่างมีประสิทธิภาพสูงสุด
ใช้ไลบรารีการโหลดรูปภาพ
คุณสามารถปรับปรุงประสิทธิภาพของแอปได้โดยใช้ไลบรารีการโหลดรูปภาพ เช่น Coil (สำหรับโปรเจ็กต์ที่ใช้ Kotlin เป็นหลัก) หรือ Glide (สำหรับโปรเจ็กต์ Java) ไลบรารีเหล่านี้จะลดการใช้งานหน่วยความจำของแอปด้วยการดำเนินการต่างๆ เช่น การแคชรูปภาพ การลดขนาดความละเอียดของกราฟิกเมื่อจำเป็น และการรีไซเคิลออบเจ็กต์กราฟิก
ลดขนาดรูปภาพ
ตรวจสอบว่าได้ใช้ขนาดรูปภาพที่เหมาะสมกับความต้องการของคุณ คุณควรหลีกเลี่ยงการโหลดรูปภาพขนาดใหญ่ที่มีความละเอียดสูงลงในคอนเทนเนอร์ขนาดเล็ก (เช่น ภาพขนาดย่อ) แต่ให้ใช้การลดขนาดความละเอียดของรูปภาพเพื่อปรับขนาดรูปภาพให้เล็กลงก่อนถอดรหัสลงในหน่วยความจำ
การลดอัตราสุ่มฝั่งไคลเอ็นต์
ไลบรารีการโหลดรูปภาพ เช่น Coil และ Glide จะลดขนาดความละเอียดของรูปภาพให้คุณโดยอัตโนมัติ คุณสามารถกำหนดค่ากลยุทธ์การลดขนาดภาพได้โดยใช้ ImageLoader (สำหรับ Coil) หรือ DownsampleStrategy (สำหรับ Glide) หากจัดการบิตแมปด้วยตนเอง คุณสามารถใช้ inSampleSize เพื่อถอดรหัสเวอร์ชันที่เล็กลงได้ หากต้องการดำเนินการนี้อย่างปลอดภัย คุณควรตั้งค่า inJustDecodeBounds เป็น true ก่อนเพื่ออ่านขนาดรูปภาพโดยไม่ต้องจัดสรรหน่วยความจำ คำนวณขนาดตัวอย่าง ตั้งค่า inSampleSize เป็นค่านั้น ตั้งค่า inJustDecodeBounds เป็น false แล้วถอดรหัสรูปภาพ
แนะนำให้ใช้การปรับขนาดฝั่งเซิร์ฟเวอร์
หากเป็นไปได้ ให้ขอขนาดรูปภาพที่ต้องการจากเซิร์ฟเวอร์แบ็กเอนด์โดยตรง ซึ่งจะช่วยลดการใช้งานเครือข่ายและพื้นที่ที่ใช้ในแคชดิสก์ ขณะเดียวกันก็ช่วยลดการใช้งานหน่วยความจำโดยหลีกเลี่ยงค่าใช้จ่ายในการปรับขนาดรูปภาพในอุปกรณ์
คุณสามารถกำหนดค่าไลบรารีให้เพิ่มขนาดมุมมองเป้าหมายต่อท้าย URL รูปภาพแบบไดนามิกได้ ตัวอย่างเช่น Coil อนุญาตให้ทำเช่นนี้โดยใช้ Interceptor ที่กำหนดเอง และ Glide รองรับการทำเช่นนี้โดยใช้ Model Loader ที่กำหนดเอง (เช่น BaseGlideUrlLoader)
หลีกเลี่ยงขนาดเลย์เอาต์ที่ไม่มีข้อจำกัด
เพื่อให้โปรแกรมโหลดรูปภาพลดขนาดตัวอย่าง (ฝั่งไคลเอ็นต์หรือฝั่งเซิร์ฟเวอร์) ได้อย่างมีประสิทธิภาพ โปรแกรมโหลดรูปภาพต้องทราบขนาดเป้าหมายก่อนที่จะดำเนินการตามคำขอ
หลีกเลี่ยงการใช้ wrapContentSize หรือปล่อยให้ขนาดไม่ถูกจำกัดใน Composable ที่โหลดรูปภาพระยะไกล หากไลบรารีเหล่านี้ไม่สามารถอนุมานขอบเขตเป้าหมายได้ ไลบรารีจะกลับไปโหลดรูปภาพขนาดเต็มต้นฉบับ
ซึ่งอาจทำให้โหลดรูปภาพขนาดใหญ่กว่าที่จำเป็นอย่างมาก ส่งผลให้มีการใช้งานหน่วยความจำและเวลาในการตอบสนองเพิ่มขึ้น
แต่ให้กำหนดขนาดที่ชัดเจนในคอมโพสิเบิลรูปภาพ (เช่น ใช้ Modifier.size) หรือกำหนดสัดส่วนภาพแทน ซึ่งช่วยให้เครื่องมือเลย์เอาต์คำนวณเป้าหมายพิกเซลที่แน่นอนล่วงหน้าได้ ซึ่งเครื่องมือโหลดรูปภาพจะใช้เพื่อขอและถอดรหัสชิ้นงานที่มีขนาดถูกต้องได้
จัดหาทรัพยากรทางเลือกสำหรับหน้าจอขนาดต่างๆ
หากคุณจัดส่งรูปภาพพร้อมกับแอป ให้พิจารณาจัดหาชิ้นงานขนาดต่างๆ สำหรับความละเอียดของอุปกรณ์ที่แตกต่างกัน ซึ่งจะช่วยลดขนาดการดาวน์โหลดของแอปในอุปกรณ์ และปรับปรุงประสิทธิภาพเนื่องจากจะโหลดรูปภาพที่มีความละเอียดต่ำลงในอุปกรณ์ที่มีความละเอียดต่ำ ดูข้อมูลเพิ่มเติมเกี่ยวกับการจัดหาบิตแมปทางเลือกสำหรับอุปกรณ์ขนาดต่างๆ ได้ที่เอกสารประกอบเกี่ยวกับบิตแมปทางเลือก
อย่าใช้ระยะห่างจากขอบโดยตรง
บางครั้งคุณอาจต้องเพิ่มระยะห่างจากขอบให้กับรูปภาพ เช่น คุณอาจต้องการให้รูปภาพล้อมรอบด้วยเส้นขอบโปร่งใสสำหรับการทำ Letterbox
ในกรณีดังกล่าว อย่าเพิ่มระยะห่างจากขอบลงในรูปภาพโดยตรง ซึ่งจะเปลี่ยนขนาดของรูปภาพ แต่ให้คงขนาดของรูปภาพไว้ตามเดิม และปรับตำแหน่งของรูปภาพบนหน้าจอโดยใช้ InsetDrawable
หรือจะเพิ่มระยะห่างจากขอบลงใน Composable หรือ View ที่มีรูปภาพก็ได้
เลือกรูปแบบของ Pixel ที่เหมาะสม
ปรับสมดุลหน่วยความจำและคุณภาพโดยเลือกรูปแบบพิกเซลที่เหมาะสม ใช้ RGB_565 เมื่อไม่ต้องการความโปร่งใส รูปแบบนี้ใช้หน่วยความจำเพียงครึ่งหนึ่งของรูปแบบ ARGB_8888 เริ่มต้น
ใน Glide คุณสามารถกำหนดค่านี้ได้โดยใช้ DecodeFormat ใน Coil คุณสามารถใช้พร็อพเพอร์ตี้ bitmapConfig ได้
ใช้เวกเตอร์เมื่อทำได้
สำหรับรูปภาพที่ประกอบด้วยรูปทรงเรขาคณิต ภาพกราฟิกเวกเตอร์จะมีขนาดเล็กกว่าบิตแมปมากและปรับขนาดได้อย่างราบรื่นสำหรับความหนาแน่นของการแสดงผลทุกรูปแบบ เมื่อเหมาะสม ให้ใช้องค์ประกอบต่างๆ เช่น ShapeDrawable เพื่อแสดงกราฟิก
ปล่อยและใช้บิตแมปซ้ำเมื่อทำได้
ไฟล์กราฟิกขนาดใหญ่จะใช้หน่วยความจำมาก คุณควรปล่อยหรือนำออบเจ็กต์กราฟิกกลับมาใช้ใหม่ทุกครั้งที่ทำได้เพื่อลดผลกระทบ
หากคุณใช้ไลบรารีการโหลดรูปภาพ โปรดตรวจสอบว่าได้ปล่อยบิตแมปไปยังพูลที่มีการจัดการของไลบรารีเมื่อไม่ต้องการใช้แล้ว ไลบรารีจะนำออบเจ็กต์มาใช้ซ้ำได้เมื่อจำเป็น และเก็บบัฟเฟอร์หน่วยความจำไว้สำหรับความต้องการในอนาคต
หากคุณจัดการกราฟิกด้วยตนเอง คุณควรปล่อยบิตแมปเมื่อใช้งานเสร็จแล้วโดยเรียกใช้ Bitmap.recycle และทิ้งการอ้างอิง Bitmap ทันที แทนที่จะพึ่งพาการเก็บขยะ
กลเม็ดเคล็ดลับอื่นๆ
ส่วนนี้จะแสดงวิธีอื่นๆ อีก 2-3 วิธีในการปรับปรุงประสิทธิภาพของแอปเมื่อจัดการกราฟิก
อย่ารวมรูปภาพขนาดใหญ่ไว้ในไฟล์ AAB/APK
สาเหตุหลักประการหนึ่งที่ทำให้ขนาดการดาวน์โหลดแอปใหญ่คือกราฟิกที่รวมอยู่ในไฟล์ AAB หรือ APK ใช้เครื่องมือตัววิเคราะห์ APK เพื่อตรวจสอบว่าคุณไม่ได้รวมไฟล์ภาพที่มีขนาดใหญ่เกินความจำเป็น ลดขนาดหรือพิจารณาวางรูปภาพไว้ในเซิร์ฟเวอร์และดาวน์โหลดเฉพาะเมื่อจำเป็น
ค้นหาบิตแมปที่ซ้ำซ้อน
หากคุณมีรูปภาพเดียวกันหลายสำเนา จะทำให้สิ้นเปลืองหน่วยความจำ คุณสามารถใช้เครื่องมือสร้างโปรไฟล์ของ Android Studio เพื่อระบุกราฟิกที่ซ้ำซ้อนได้ ใช้เครื่องมือวิเคราะห์ฮีปดัมป์เพื่อบันทึกฮีปดัมป์ และกรองผลลัพธ์โดยเลือกการตั้งค่าบิตแมปที่ซ้ำกัน
เมื่อใช้ ImageBitmap ให้เรียกใช้ prepareToDraw ก่อนวาด
เมื่อใช้ ImageBitmap หากต้องการเริ่มกระบวนการอัปโหลดพื้นผิวไปยัง GPU ให้เรียกใช้ ImageBitmap#prepareToDraw() ก่อนที่จะวาดจริง ซึ่งจะช่วยให้ GPU เตรียมพื้นผิวและปรับปรุงประสิทธิภาพการแสดงภาพบนหน้าจอ ไลบรารีการโหลดรูปภาพส่วนใหญ่จะทำการเพิ่มประสิทธิภาพนี้อยู่แล้ว แต่หากคุณทำงานกับคลาส ImageBitmap ด้วยตนเอง ก็ควรคำนึงถึงเรื่องนี้ด้วย
แนะนำให้ส่ง Int DrawableRes หรือ URL เป็นพารามิเตอร์ไปยัง Composable แทน Painter
เนื่องจากความซับซ้อนในการจัดการรูปภาพ (เช่น การเขียนฟังก์ชันที่เท่ากันสำหรับ Bitmaps จะมีค่าใช้จ่ายในการประมวลผลสูง) API Painter จึงไม่ได้ทำเครื่องหมายไว้อย่างชัดเจนว่าเสถียรด้วยคำอธิบายประกอบ @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