0Pricing
Android Academy · บทเรียน

เหตุใดจึงต้องแบ่งหน้า

ต้นทุนของการโหลดทุกอย่างพร้อมกัน

เหตุใดจึงต้องแบ่งหน้า เป็นบทเรียน Android Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Android Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Android Academy มีบทเรียนทั้งหมด 4 บทเรียน

โหลดทุกอย่างมีต้นทุนสูง

ลองนึกภาพฟีดที่มีรายการ 50,000 รายการ หากคุณดึงข้อมูลและเก็บทุกรายการไว้พร้อมกัน จะสิ้นเปลืองหน่วยความจำ ทำให้เครือข่ายทำงานช้าลง และทำให้ UI ค้างขณะวิเคราะห์การตอบกลับ

ผู้ใช้ส่วนใหญ่เลื่อนดูเพียงไม่กี่หน้าจอแรกเท่านั้น การแบ่งหน้า หมายถึงการโหลดข้อมูลเป็นส่วนเล็ก ๆ (หน้า) ขณะที่ผู้ใช้เลื่อน แทนที่จะโหลดทั้งหมดตั้งแต่ต้น

ปัญหาในโค้ด

วิธีง่าย ๆ คือโหลดรายการทั้งหมดเข้าไปไว้ในหน่วยความจำ แม้เซิร์ฟเวอร์จะรองรับ แต่การตอบกลับอาจมีขนาดใหญ่มาก และรายการที่แยกวิเคราะห์แล้วอาจกินพื้นที่ฮีปส่วนใหญ่

รูปแบบนี้ไม่สามารถรองรับข้อมูลที่เพิ่มขึ้นได้ดี และเสี่ยงทำให้เกิด OutOfMemoryError เมื่อชุดข้อมูลมีขนาดใหญ่

// Anti-pattern: load the whole table at once
suspend fun loadAllArticles(): List<Article> {
    // Could be tens of thousands of rows / megabytes of JSON
    return api.getArticles(limit = 50_000)
}

// UI holds ALL of them in memory at once
val articles = loadAllArticles()
LazyColumn {
    items(articles) { article -> ArticleRow(article) }
}

หน้าตาหนึ่งหน้าจะเป็นอย่างไร

หน้า คือส่วนย่อยขนาดเล็กของชุดข้อมูล โดยมักมี 20–50 รายการ เซิร์ฟเวอร์จะส่งคืนหนึ่งหน้าพร้อมตัวชี้ไปยังหน้าถัดไป ซึ่งอาจเป็นตัวเลข ออฟเซ็ต หรือโทเค็น

แอปจะเก็บไว้ในหน่วยความจำเพียงไม่กี่หน้า และทิ้งหน้าเก่าเมื่อผู้ใช้เลื่อนผ่านไป

data class ArticlePage(
    val items: List<Article>,
    val nextKey: Int?   // null means no more pages
)

// Example REST call returning one page
suspend fun getArticlePage(page: Int, size: Int = 20): ArticlePage

ทำความรู้จัก Paging 3

Paging 3 คือไลบรารี Jetpack ที่จัดการส่วนที่ซับซ้อนของการแบ่งหน้าให้คุณทั้งหมด:

  • ขอหน้าถัดไปเมื่อผู้ใช้เลื่อนเข้าใกล้ท้ายรายการ
  • เก็บเฉพาะช่วงของรายการไว้ในหน่วยความจำ
  • เปิดเผยสถานะการโหลดและข้อผิดพลาด
  • รองรับ coroutine, Flow และ Jetpack Compose ในตัว

การเพิ่มการพึ่งพา

Paging 3 เผยแพร่เป็นอาร์ติแฟกต์แยกกัน ได้แก่ รันไทม์และการเชื่อมต่อกับ Compose ให้เพิ่มอาร์ติแฟกต์เหล่านี้ใน build.gradle.kts ของโมดูล

อาร์ติแฟกต์ paging-compose มีตัวช่วยสำหรับ LazyColumn

// build.gradle.kts (module)
dependencies {
    val pagingVersion = "3.3.6"
    implementation("androidx.paging:paging-runtime:$pagingVersion")
    implementation("androidx.paging:paging-compose:$pagingVersion")
}

องค์ประกอบหลักสามส่วน

Paging 3 มีประเภทหลักสามประเภทที่คุณจะใช้ซ้ำแล้วซ้ำอีก:

  • PagingSource - รู้วิธีโหลดหนึ่งหน้าจากแหล่งข้อมูล
  • Pager - กำหนดค่าและสร้างสตรีมข้อมูลแบบแบ่งหน้า
  • PagingData - คอนเทนเนอร์ของรายการ ซึ่งปล่อยออกมาเป็น Flow

เราจะสร้างแต่ละส่วนในบทเรียนถัดไป

PagingData และ Flow

รีโพซิทอรีของคุณเปิดเผย Flow<PagingData<T>> ส่วน UI จะรวบรวมข้อมูลและแสดงรายการที่โหลดอยู่ในขณะนั้น

คุณไม่ต้องประกอบรายการทั้งหมดด้วยตนเอง เพราะ Paging จะสตรีมรายการเข้าสู่ UI ตามต้องการ

import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import kotlinx.coroutines.flow.Flow

class ArticleRepository(private val api: ArticleApi) {
    fun articles(): Flow<PagingData<Article>> = Pager(
        config = PagingConfig(pageSize = 20)
    ) {
        ArticlePagingSource(api)
    }.flow
}

PagingConfig ควบคุมช่วงข้อมูล

PagingConfig ใช้ปรับวิธีที่ Paging โหลดและเก็บข้อมูล:

  • pageSize - จำนวนรายการต่อคำขอหนึ่งหน้า
  • prefetchDistance - ระยะห่างจากขอบที่ใช้เรียกการโหลดครั้งถัดไป
  • enablePlaceholders - แสดงช่องตัวแทนสำหรับรายการที่ยังไม่ได้โหลด
  • maxSize - จำกัดจำนวนรายการที่เก็บไว้ในหน่วยความจำ
import androidx.paging.PagingConfig

val config = PagingConfig(
    pageSize = 20,
    prefetchDistance = 5,
    enablePlaceholders = false,
    maxSize = 100
)

เครือข่ายหรือฐานข้อมูล หรือทั้งสองอย่าง

PagingSource สามารถดึงข้อมูลจาก REST API ฐานข้อมูล Room หรือแหล่งข้อมูลแบบกำหนดเองใด ๆ

หากต้องการประสบการณ์แบบออฟไลน์เป็นหลักอย่างแท้จริง ให้รวมทั้งสองอย่างด้วย RemoteMediator (ซึ่งจะกล่าวถึงในบทเรียนสุดท้าย): เครือข่ายจะเติมข้อมูลลงในฐานข้อมูลภายในเครื่อง และ UI จะแบ่งหน้าข้อมูลจากฐานข้อมูลนั้น

เลื่อนดูได้ลื่นไหล ใช้หน่วยความจำน้อยลง

เมื่อใช้การแบ่งหน้า การเลื่อนจะยังคงลื่นไหล เพราะแต่ละคำขอมีขนาดเล็ก และช่วงข้อมูลในหน่วยความจำถูกจำกัดด้วย maxSize

ตัวบ่งชี้การโหลดและข้อผิดพลาดมีให้ผ่าน LoadState โดยไม่ต้องเขียนเพิ่ม คุณจึงแสดงตัวหมุนด้านล่างหรือปุ่มลองใหม่ได้โดยไม่ต้องจัดการสถานะด้วยตนเอง

// LoadState exposes loading/error per direction
when (val state = adapterLoadState.append) {
    is LoadState.Loading -> showBottomSpinner()
    is LoadState.Error -> showRetry(state.error)
    is LoadState.NotLoading -> hideBottomSpinner()
}

เมื่อใดที่ควรใช้การแบ่งหน้า

เลือกใช้ Paging 3 เมื่อ:

  • ชุดข้อมูลมีขนาดใหญ่หรือไม่มีขอบเขตแน่นอน เช่น ฟีด ผลการค้นหา หรือประวัติแชต
  • ข้อมูลมาจาก API เครือข่ายที่ส่งคืนข้อมูลเป็นหน้าหรือเคอร์เซอร์
  • คุณต้องการ UI สำหรับการโหลดและข้อผิดพลาดโดยอัตโนมัติ รวมถึงการควบคุมหน่วยความจำ

สำหรับรายการสั้น ๆ ที่มีจำนวนตายตัว เช่น เมนูการตั้งค่า การใช้ LazyColumn ธรรมดาจะง่ายกว่าและเพียงพออย่างสมบูรณ์

ตรวจสอบความเข้าใจ

ประโยชน์หลักของการใช้ Paging 3 กับรายการขนาดใหญ่คืออะไร

สรุป: เหตุผลที่ควรใช้การแบ่งหน้า

คุณได้เรียนรู้ว่าการโหลดทุกอย่างพร้อมกันส่งผลเสียต่อประสิทธิภาพและหน่วยความจำอย่างไร และ Paging 3 แก้ปัญหานี้อย่างไร

  • โหลดข้อมูลเป็นหน้าขนาดเล็กขณะผู้ใช้เลื่อนดู
  • องค์ประกอบหลักสามส่วนคือ PagingSource, Pager และ PagingData
  • PagingConfig ใช้ปรับขนาดหน้า การโหลดล่วงหน้า และช่วงข้อมูลในหน่วยความจำ
  • Paging ทำงานร่วมกับ coroutine, Flow และ Compose พร้อมมีสถานะการโหลดและข้อผิดพลาดให้โดยไม่ต้องเขียนเพิ่ม

ถัดไป: การสร้าง PagingSource และเชื่อมต่อเข้ากับ Pager

คำถามที่พบบ่อย

บทเรียน “เหตุใดจึงต้องแบ่งหน้า” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “เหตุใดจึงต้องแบ่งหน้า” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Android Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Android Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “เหตุใดจึงต้องแบ่งหน้า”

ต้นทุนของการโหลดทุกอย่างพร้อมกัน คุณปฏิบัติ Android Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Android Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Android Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน

บทเรียน “เหตุใดจึงต้องแบ่งหน้า” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Android Academy นี้ได้ไหม

ได้ บทเรียน Android Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. เหตุใดจึงต้องแบ่งหน้า
  2. PagingSource และ Pager
  3. การแบ่งหน้าในรายการ Compose
  4. RemoteMediator และการแคช
← กลับไปที่ Android Academy