การทำงานกับรูปภาพอาจทำให้เกิดปัญหาด้านประสิทธิภาพได้อย่างรวดเร็วหากคุณไม่ระมัดระวัง แม้แต่กราฟิกขนาดเล็กในรูปแบบที่บีบอัด เช่น JPG หรือ PNG ก็อาจกลายเป็นบิตแมปขนาดใหญ่เมื่อถอดรหัสเพื่อแสดงผล หากคุณใช้กราฟิกอย่างไม่มีประสิทธิภาพ คุณอาจพบปัญหาเกี่ยวกับหน่วยความจำซึ่งอาจส่งผลต่อประสิทธิภาพของแอปและแอปอื่นๆ ในอุปกรณ์ ทำตามแนวทางปฏิบัติแนะนำเหล่านี้เพื่อให้แอปทำงานได้อย่างเต็มประสิทธิภาพ
ใช้ไลบรารีการโหลดรูปภาพ
คุณปรับปรุงประสิทธิภาพของแอปได้โดยใช้ไลบรารีการโหลดรูปภาพ เช่น Coil (สําหรับโปรเจ็กต์ที่ใช้ Kotlin เป็นภาษาหลัก) หรือ Glide (สําหรับโปรเจ็กต์ Java) ไลบรารีเหล่านี้จะลดการใช้งานหน่วยความจําของแอปโดยการแคช รูปภาพ ลดขนาดกราฟิกเมื่อจําเป็น และรีไซเคิลออบเจ็กต์กราฟิก
ลดขนาดตัวอย่างรูปภาพ
โปรดใช้ขนาดรูปภาพที่เหมาะสมกับความต้องการของคุณ คุณควรหลีกเลี่ยง การโหลดรูปภาพขนาดใหญ่ความละเอียดสูงลงในคอนเทนเนอร์ขนาดเล็ก (เช่น ภาพปก) แต่ให้ใช้การลดขนาดตัวอย่างเพื่อลดขนาดรูปภาพก่อน ถอดรหัสลงในหน่วยความจำแทน
การดาวน์แซมปลิงฝั่งไคลเอ็นต์
ไลบรารีการโหลดรูปภาพ เช่น Coil และ Glide จะจัดการการลดขนาดตัวอย่างให้คุณโดยอัตโนมัติ คุณสามารถกำหนดค่ากลยุทธ์การลดขนาดตัวอย่างได้โดยใช้ ImageLoader (สำหรับ Coil) หรือ DownsampleStrategy (สำหรับ Glide) หากคุณจัดการบิตแมปด้วยตนเอง คุณสามารถใช้ inSampleSize เพื่อถอดรหัสเวอร์ชันที่เล็กลงได้ หากต้องการทำอย่างปลอดภัย คุณควรกำหนด inJustDecodeBounds เป็น true ก่อนเพื่ออ่านขนาดรูปภาพโดยไม่ต้องจัดสรรหน่วยความจำ คำนวณขนาดตัวอย่าง กำหนด inSampleSize เป็นค่านั้น กำหนด inJustDecodeBounds เป็น false แล้วจึงถอดรหัสรูปภาพ
ใช้การปรับขนาดฝั่งเซิร์ฟเวอร์
หากเป็นไปได้ ให้ขอขนาดรูปภาพที่แน่นอนที่คุณต้องการจากเซิร์ฟเวอร์แบ็กเอนด์โดยตรง ซึ่งจะช่วยลดการใช้เครือข่ายและร่องรอยแคชดิสก์ ขณะเดียวกันก็ช่วยให้มีการใช้งานหน่วยความจำน้อยลงด้วยการหลีกเลี่ยงค่าใช้จ่ายด้านหน่วยความจำในการปรับขนาดรูปภาพในอุปกรณ์
คุณกำหนดค่าไลบรารีให้ต่อท้ายขนาดมุมมองเป้าหมายแบบไดนามิกลงใน
URL รูปภาพได้ ตัวอย่างเช่น Coil อนุญาตให้ใช้ตัวสกัดกั้นที่กำหนดเอง และ Glide
รองรับการใช้ตัวโหลดโมเดลที่กำหนดเอง (เช่น BaseGlideUrlLoader)
หลีกเลี่ยงขนาดเลย์เอาต์ที่ไม่จำกัด
โปรแกรมโหลดรูปภาพต้องทราบขนาดเป้าหมายก่อนที่จะดำเนินการตามคำขอ เพื่อให้ลดขนาดตัวอย่าง (ฝั่งไคลเอ็นต์หรือฝั่งเซิร์ฟเวอร์) ได้อย่างมีประสิทธิภาพ
หลีกเลี่ยงการใช้ wrapContentSize หรือปล่อยให้มิติข้อมูลไม่ถูกจำกัดใน Composables ที่โหลดรูปภาพจากระยะไกล หากไลบรารีเหล่านี้ไม่สามารถอนุมานขอบเขตเป้าหมายได้ ระบบจะกลับไปโหลดรูปภาพขนาดเต็มต้นฉบับ ซึ่งอาจทำให้โหลดรูปภาพที่มีขนาดใหญ่กว่าที่จำเป็นมาก ส่งผลให้การใช้งานหน่วยความจำและความหน่วงเพิ่มขึ้น
แต่ให้ตั้งค่าขนาดที่ชัดเจนใน Composable ของรูปภาพ (เช่น ใช้
Modifier.size) หรือกำหนดสัดส่วนภาพ วิธีนี้จะช่วยให้เครื่องมือเลย์เอาต์
คำนวณเป้าหมายพิกเซลที่แน่นอนล่วงหน้าได้ จากนั้นโปรแกรมโหลดรูปภาพจะใช้เพื่อ
ขอและถอดรหัสชิ้นงานที่มีขนาดถูกต้องได้
ระบุแหล่งข้อมูลสำรองสำหรับขนาดหน้าจอต่างๆ
หากคุณจัดส่งรูปภาพพร้อมกับแอป ให้พิจารณาจัดหาชิ้นงานที่มีขนาดแตกต่างกัน สำหรับความละเอียดของอุปกรณ์ที่แตกต่างกัน ซึ่งจะช่วยลดขนาดการดาวน์โหลด ของแอปในอุปกรณ์ และปรับปรุงประสิทธิภาพเนื่องจากจะโหลดรูปภาพที่มีความละเอียดต่ำกว่า ในอุปกรณ์ที่มีความละเอียดต่ำกว่า ดูข้อมูลเพิ่มเติมเกี่ยวกับการระบุบิตแมปสำรองสำหรับอุปกรณ์ขนาดต่างๆ ได้ที่เอกสารประกอบเกี่ยวกับบิตแมปสำรอง
อย่าใช้ระยะห่างโดยตรง
ในบางครั้งคุณอาจต้องเพิ่มระยะห่างจากขอบให้กับรูปภาพ เช่น คุณอาจต้องการให้รูปภาพมีเส้นขอบโปร่งใสเพื่อทำเล็ตเตอร์บ็อกซ์
ในกรณีดังกล่าว อย่าเพิ่มระยะห่างจากขอบให้กับรูปภาพโดยตรง ซึ่งจะเปลี่ยน
ขนาดของรูปภาพ ให้คงขนาดของรูปภาพไว้ตามเดิม
และปรับตำแหน่งของรูปภาพบนหน้าจอโดยใช้ InsetDrawable
หรือคุณจะเพิ่มระยะห่างจากขอบลงใน Composable หรือ View ที่มีรูปภาพก็ได้
เลือกรูปแบบพิกเซลที่เหมาะสม
รักษาสมดุลระหว่างหน่วยความจำและคุณภาพโดยเลือกรูปแบบพิกเซลที่เหมาะสม ใช้ RGB_565
เมื่อไม่ต้องการความโปร่งใส เนื่องจากรูปแบบนี้ใช้หน่วยความจำเพียงครึ่งเดียวของรูปแบบ ARGB_8888 เริ่มต้น
ใน Glide คุณกำหนดค่านี้ได้โดยใช้ DecodeFormat ใน Coil คุณสามารถ
ใช้พร็อพเพอร์ตี้ bitmapConfig ได้
ใช้เวกเตอร์หากทำได้
สำหรับรูปภาพที่ประกอบด้วยรูปร่างทางเรขาคณิต ภาพกราฟิกเวกเตอร์จะมีขนาดเล็กกว่าบิตแมปมาก และปรับขนาดได้อย่างราบรื่นสำหรับความหนาแน่นในการแสดงผลทุกระดับ เมื่อเหมาะสม ให้ใช้องค์ประกอบ
เช่น ShapeDrawable เพื่อแสดงกราฟิก
เผยแพร่และนำบิตแมปกลับมาใช้ใหม่เมื่อทำได้
ไฟล์กราฟิกขนาดใหญ่อาจใช้หน่วยความจำมาก คุณควรปล่อยหรือนำออบเจ็กต์กราฟิกกลับมาใช้ใหม่ทุกครั้งที่ทำได้เพื่อลดผลกระทบ
หากใช้ไลบรารีการโหลดรูปภาพ ให้ตรวจสอบว่าได้ปล่อย บิตแมปไปยังพูลที่มีการจัดการของไลบรารีเมื่อไม่ต้องการใช้แล้ว ไลบรารีจะนำออบเจ็กต์กลับมาใช้ใหม่ได้เมื่อจำเป็น และจะเก็บหน่วยความจำบัฟเฟอร์ไว้สำหรับความต้องการในอนาคต
หากจัดการกราฟิกด้วยตนเอง คุณควรปล่อยบิตแมปเมื่อใช้งานเสร็จแล้วโดยเรียกใช้ Bitmap.recycle และทิ้งการอ้างอิง Bitmap ทันที แทนที่จะอาศัยระบบจัดการหน่วยความจำที่ไม่ใช้แล้ว
เคล็ดลับและคำแนะนำอื่นๆ
ส่วนนี้แสดงวิธีอื่นๆ บางส่วนในการปรับปรุงประสิทธิภาพของแอปเมื่อ จัดการกราฟิก
อย่าแพ็กเกจรูปภาพขนาดใหญ่กับไฟล์ AAB/APK
สาเหตุหลักประการหนึ่งที่ทำให้ขนาดการดาวน์โหลดแอปมีขนาดใหญ่คือกราฟิกที่ รวมอยู่ในไฟล์ AAB หรือ APK ใช้เครื่องมือเครื่องมือวิเคราะห์ APK เพื่อให้แน่ใจว่า คุณไม่ได้รวมไฟล์รูปภาพที่มีขนาดใหญ่กว่าที่จำเป็น ลดขนาดหรือ พิจารณาจัดเก็บรูปภาพไว้ในเซิร์ฟเวอร์และดาวน์โหลดเฉพาะเมื่อจำเป็น
ค้นหาบิตแมปที่ซ้ำซ้อน
หากมีสำเนารูปภาพเดียวกันหลายรายการ จะทำให้สิ้นเปลืองหน่วยความจำ คุณสามารถใช้เครื่องมือสร้างโปรไฟล์ของ 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