Coil เป็นไลบรารีสำหรับโหลดรูปภาพบน Android ที่เขียนด้วย Kotlin และสร้างบน coroutines ตาม เอกสารทางการ ไลบรารีรองรับ Memory Cache, Disk Cache และการแปลงด้วยการเร่งฮาร์ดแวร์ Coil โดดเด่นด้วยขนาด APK ที่เล็กที่สุด (ประมาณ 150 KB) และความเข้ากันได้อย่างสมบูรณ์กับ Jetpack Compose
ประเด็นสำคัญ
Coil (Coroutine Image Loader) เป็นไลบรารีสำหรับโหลดรูปภาพบน Android ที่เขียนด้วย Kotlin ทั้งหมดและใช้ coroutines สำหรับการดำเนินการแบบอะซิงโครนัส โดยมี API แบบรวมสำหรับโหลดรูปภาพบิตแมปจากเครือข่าย ทรัพยากร ระบบไฟล์ และ Content Provider พร้อมการแคชอัตโนมัติหลายระดับ
แตกต่างจาก Glide และ Picasso ตรงที่ Coil ใช้ Kotlin Coroutines แทนการเรียกกลับแบบลูกโซ่ ทำให้โค้ดเป็นเชิงเส้นและคาดการณ์ได้มากขึ้น การดำเนินการโหลดและถอดรหัสทั้งหมดทำงานบนเธรดพื้นหลังผ่าน dispatcher Dispatchers.IO และผลลัพธ์จะถูกส่งไปยังเธรดหลักโดยไม่ต้องสลับอย่างชัดเจน
Coil รองรับการแปลง (Round, Blur, Grayscale), แอนิเมชันการเปลี่ยนภาพ, SVG และ GIF รวมถึง Target ที่กำหนดเองสำหรับการแสดงผลที่ไม่ได้มาตรฐาน ตามข้อมูลจาก Google I/O 2023 Coil ได้รับคำแนะนำในบทช่วยสอน Jetpack Compose อย่างเป็นทางการควบคู่กับ Glide
ImageLoader เป็นส่วนประกอบหลักของ Coil ที่รับผิดชอบในการดำเนินการคำขอโหลดและจัดการแคช แต่ละอินสแตนซ์มีการอ้างอิงถึง MemoryCache, DiskCache, BitmapPool และพูล coroutines โดยค่าเริ่มต้น จะใช้ซิงเกิลตันที่สร้างผ่าน Coil.imageLoader(context)
ImageRequest เป็นออบเจกต์ที่อธิบายคำขอโหลดรูปภาพเดียว: แหล่งข้อมูล (URL, URI, ทรัพยากร Int), ImageView หรือ Target เป้าหมาย, การแปลง, การตั้งค่าแคช และ placeholder ImageRequest ถูกสร้างผ่าน builder ซึ่งรับประกันความยืดหยุ่นและความอ่านง่าย
val request = ImageRequest.Builder(context)
.data("https://example.com/image.jpg")
.crossfade(true)
.size(512, 512)
.transformations(listOf(RoundedCornersTransformation(12f)))
.memoryCachePolicy(CachePolicy.ENABLED)
.diskCachePolicy(CachePolicy.ENABLED)
.target(imageView)
.build()
หลังจากสร้าง ImageRequest จะถูกส่งไปยัง ImageLoader ผ่าน enqueue หรือ execute เมธอด enqueue เริ่ม coroutine และส่งคืน Disposable ซึ่งอนุญาตให้ยกเลิกการโหลดเมื่อออกจากหน้าจอ เมธอด execute เป็นฟังก์ชัน suspend ที่ส่งคืน Result โดยตรง
ImageLoader ตรวจสอบ MemoryCache, DiskCache ตามลำดับ และเมื่อทั้งสองไม่พบข้อมูลจะดำเนินการขอเครือข่ายผ่าน HttpEngine หลังจากการโหลด ไบต์จะถูกถอดรหัสเป็น Bitmap โดยพิจารณาขนาดเป้าหมาย ใช้การแปลง ผลลัพธ์จะถูกเก็บในแคชทั้งสองและส่งต่อไปยัง Target
Coil สร้างบนสถาปัตยกรรมแบบ component ที่สามารถเปลี่ยนส่วนใดๆ ได้ผ่าน Dependency Injection component ทั้งหมดลงทะเบียนใน ImageLoaderFactory และส่งไปยังคอนสตรักเตอร์ของ ImageLoader ผ่าน builder
ImageLoader เป็นจุดเริ่มต้นสำหรับการดำเนินการโหลดทั้งหมด แต่ละอินสแตนซ์ประกอบด้วยพูล coroutines, BitmapPool, MemoryCache, DiskCache และรายการตัวสกัดกั้น โดยค่าเริ่มต้น จะสร้างอินสแตนซ์ส่วนกลางหนึ่งอัน แต่สำหรับการทดสอบหน่วย สามารถสร้างอินสแตนซ์แยกกับแคชที่แยกได้
MemoryCache เป็นแคชในหน่วยความจำแบบ LRU (Least Recently Used) ที่เก็บออบเจกต์ Bitmap ที่ถอดรหัสแล้ว ขนาดสูงสุดโดยค่าเริ่มต้นคือ 25% ของหน่วยความจำที่มีของแอปพลิเคชัน แต่ไม่น้อยกว่า 32 MB คีย์แคชสร้างจาก URL + ขนาด + การแปลง ซึ่งป้องกันการดึงรูปภาพที่ล้าสมัย
DiskCache เป็นแคชแบบไฟล์สำหรับข้อมูลดิบ (JPEG, PNG, WebP) และเมตาดาต้าที่ถอดรหัสแล้ว อยู่ในไดเรกทอรีแคชของแอปพลิเคชันและรองรับการล้างอัตโนมัติเมื่อเกินขีดจำกัด การดำเนินการดิสก์ทำผ่าน DiskCache.Builder พร้อมการกำหนดค่าไดเรกทอรีและขนาดสูงสุด
Coil ใช้กลยุทธ์การแคชหลายระดับที่ลดคำขอเครือข่ายและเร่งการแสดงรูปภาพ แต่ละระดับมีวัตถุประสงค์และอายุข้อมูลของตัวเอง
| ระดับ | ประเภทการจัดเก็บ | อายุ | ขนาดเริ่มต้น |
|---|---|---|---|
| Memory Cache | Bitmap ใน RAM | จนกว่าจะถูก LRU แทนที่ | 25% ของ heap จาก 32 MB |
| Disk Cache | ไฟล์ JPEG/WebP | จนเกินขีดจำกัด | 250 MB |
| Http Cache | การตอบสนอง OkHttp | ตามหัวเรื่อง Cache-Control | ขึ้นอยู่กับ HTTP client |
Memory Cache ให้การเข้าถึง Bitmap ที่ถอดรหัสแล้วทันที Disk Cache รับประกันว่าแอปทำงานได้โดยไม่ต้องใช้เครือข่าย (offline-first) หลังจากโหลดครั้งแรก Http Cache ในระดับ OkHttp จัดการคำขอแบบมีเงื่อนไขด้วย ETag และ If-Modified-Since
นโยบายการแคชถูกกำหนดค่าต่อคำขอผ่าน CachePolicy ด้วยสามค่า: ENABLED, READ_ONLY, WRITE_ONLY, DISABLED ตัวอย่างเช่น สำหรับอวาตาร์ผู้ใช้ สามารถตั้งค่า READ_ONLY สำหรับ Memory Cache และ ENABLED สำหรับ Disk Cache
Coil มีวิธีการรวมหลายวิธีขึ้นอยู่กับสถาปัตยกรรมแอปพลิเคชัน มาดูสามสถานการณ์หลักพร้อมตัวอย่างโค้ดที่ใช้งานได้
load เป็นฟังก์ชันส่วนขยายสำหรับ ImageView วิธีที่ง่ายที่สุดในการโหลดรูปภาพในบรรทัดเดียว ฟังก์ชันรับ URL, URI, ทรัพยากร Int หรือ File พร้อมพารามิเตอร์เพิ่มเติมทั้งหมดผ่านตัวกำหนดค่า lambda
imageView.load("https://example.com/photo.jpg") {
crossfade(true)
placeholder(R.drawable.placeholder)
error(R.drawable.error)
size(300, 300)
transformations(CircleCropTransformation())
}
เมธอด load ส่งคืน Disposable ซึ่งสามารถยกเลิกได้ใน onDestroy หรือเมื่อใช้ View ซ้ำ ซึ่งป้องกันการรั่วไหลของหน่วยความจำและคำขอเครือข่ายที่ไม่จำเป็นระหว่างการเลื่อนรายการอย่างรวดเร็ว
AsyncImage เป็นฟังก์ชัน composable สำหรับโหลดรูปภาพใน UI แบบประกาศ รองรับแหล่งข้อมูลใดๆ และพารามิเตอร์เสริมสามตัวสำหรับสถานะ: placeholder, error และ success
@Composable
fun NetworkImage(url: String) {
AsyncImage(
model = url,
contentDescription = "รูปภาพเครือข่าย",
placeholder = ColorPainter(Color.Gray),
error = ColorPainter(Color.Red)
)
}
SubcomposeAsyncImage เป็นเวอร์ชันที่ยืดหยุ่นกว่าซึ่งอนุญาตให้ปรับแต่งการแสดงผลระหว่างการโหลดผ่านช่องเนื้อหา มีประโยชน์สำหรับโครงกระดูก (shimmer) และแถบความคืบหน้า
หาก ImageView หรือ AsyncImage ไม่เหมาะสม คุณสามารถใช้ Target ที่มีเมธอด onSuccess เดียวที่รับ Bitmap ใช้สำหรับโหลดใน Notification, RemoteViews หรือพื้นผิว OpenGL
val target = object : BitmapTarget() {
override fun onSuccess(result: Bitmap) {
notificationRemoteView.setImageViewBitmap(R.id.icon, result)
}
}
imageLoader.enqueue(
ImageRequest.Builder(context)
.data(url)
.target(target)
.build()
)
การเลือกไลบรารีโหลดรูปภาพขึ้นอยู่กับความต้องการของโปรเจกต์ Coil แข่งขันกับ Glide และ Picasso ซึ่งแต่ละแห่งมีจุดแข็ง การเปรียบเทียบคุณสมบัติหลักแสดงอยู่ในตาราง
| คุณสมบัติ | Coil | Glide | Picasso |
|---|---|---|---|
| ภาษา | Kotlin (100%) | Java + Kotlin | Java |
| ขนาด APK | ~150 KB | ~500 KB | ~120 KB |
| Coroutines | ในตัว | ไม่มี (callback) | ไม่มี (callback) |
| Jetpack Compose | รองรับโดยตรง | ผ่าน accompanist | บุคคลที่สาม |
| GIF/WebP | ใช่ (ในตัว) | ใช่ (ในตัว) | ไม่ |
| คำแนะนำจาก Google | ใช่ (I/O 2023) | ใช่ | ไม่ |
สำหรับโปรเจกต์ใหม่บน Kotlin และ Jetpack Compose Coil กลายเป็นตัวเลือกโดยธรรมชาติเนื่องจากการไม่พึ่งพา coroutines เพิ่มเติมและขนาดที่เล็กที่สุด Glide ยังคงเป็นที่นิยมสำหรับสถานการณ์ที่ซับซ้อนด้วยแอนิเมชันและตัวอย่างวิดีโอ Picasso ด้อยกว่าทั้งสองในด้านฟังก์ชันการทำงาน แต่ชนะในด้านความเรียบง่าย
การเพิ่ม Coil ในโปรเจกต์ Android ทำผ่านการพึ่งพา Gradle หลังจากเพิ่ม ไลบรารีจะลงทะเบียน ImageLoader โดยอัตโนมัติผ่าน ContentProvider ดังนั้นไม่จำเป็นต้องเริ่มต้นด้วยตนเองใน Application หากต้องการปรับแต่ง จะสร้าง ImageLoader ที่กำหนดเองผ่าน builder
// build.gradle.kts (app module)
dependencies {
implementation("io.coil-kt:coil:2.6.0")
// สำหรับ Jetpack Compose เพิ่มเติม:
implementation("io.coil-kt:coil-compose:2.6.0")
// สำหรับการรองรับ SVG:
implementation("io.coil-kt:coil-svg:2.6.0")
// สำหรับการรองรับ GIF:
implementation("io.coil-kt:coil-gif:2.6.0")
}
สำหรับการปรับแต่ง ImageLoader จะใช้ ImageLoaderFactory — ซิงเกิลตันที่สร้างใน Application.onCreate ในโรงงาน คุณสามารถกำหนดค่าขีดจำกัดแคช, HTTP client, ตัวถอดรหัสที่กำหนดเอง และการบันทึก โดยค่าเริ่มต้น Coil ใช้ OkHttp พร้อมพูลการเชื่อมต่อที่พร้อมใช้งาน
class App : Application(), ImageLoaderFactory {
override fun newImageLoader(): ImageLoader {
return ImageLoader.Builder(this)
.memoryCache {
MemoryCache.Builder()
.maxSizePercent(0.25)
.build()
}
.diskCache {
DiskCache.Builder()
.directory(cacheDir.resolve("coil_cache"))
.maxSizeBytes(512 * 1024 * 1024)
.build()
}
.build()
}
}
คำถามที่พบบ่อย
Coil เป็นไลบรารีโหลดรูปภาพสำหรับ Android ที่เขียนด้วย Kotlin โดยใช้ coroutines ใช้สำหรับโหลด แคช และแสดงรูปภาพบิตแมปแบบอะซิงโครนัสจากเครือข่าย ทรัพยากร หรือระบบไฟล์
Coil เขียนด้วย Kotlin 100% และใช้ coroutines แทนกลไก callback ของ Glide Coil มีขนาด APK เล็กกว่า (~150 KB เทียบกับ ~500 KB) และรองรับ Jetpack Compose โดยตรงผ่าน AsyncImage
เพิ่มการพึ่งพา io.coil-kt:coil:2.6.0 ใน build.gradle.kts สำหรับ Jetpack Compose ให้เพิ่ม io.coil-kt:coil-compose:2.6.0 ด้วย ไลบรารีจะลงทะเบียน ImageLoader โดยอัตโนมัติผ่าน ContentProvider
Coil รองรับ JPEG, PNG, WebP, BMP, SVG (ผ่านโมดูล coil-svg) และ GIF (ผ่านโมดูล coil-gif) รูปแบบ AVIF และ HEIF รองรับผ่านตัวถอดรหัสที่กำหนดเองบนอุปกรณ์ที่ใช้ Android 10+
แคชถูกกำหนดค่าผ่าน ImageLoader.Builder: memoryCache ระบุเปอร์เซ็นต์ของ heap, diskCache ระบุเส้นทางและขีดจำกัดเป็นไบต์ นโยบายแคช (ENABLED, DISABLED, READ_ONLY) ถูกกำหนดค่าต่อคำขอผ่าน CachePolicy
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ