[Android] ลองใช้งาน Paging3 Library ในการเรียกข้อมูลจาก API มาแสดงผล และทำ Load More แบบอัตโนมัติ

Theerapong.Kha
te<h @TDG
Published in
3 min readNov 2, 2020

แอปส่วนใหญ่ในปัจจุบันจะต้องมีการเรียกข้อมูลจาก API เพื่อมาแสดงผล และสมมุติว่าเรามีข้อมูลมากมาย คงเป็นไปไม่ได้ที่จะนำข้อมูลทั้งหมดเหล่านั้นโหลดมาแสดงผลในทีเดียว แต่จะทำการหยิบข้อมูลทีละส่วนเล็ก ๆ มาแสดงผล (small chunk) และเมื่อ user scroll ไปจนถึงข้อมูลอันสุดท้ายในลิสต์ แอปก็จะทำการ load more เพื่อเรียกข้อมูลถัดไปมาแสดงผลให้อัตโนมัติ และ google ก็ได้แนะนำ ตัว Paging3 Library เพื่อมาตอบโจทย์นักพัฒนาในเรื่องนี้

ในบทความนี้จะพามาทดลองใช้งาน Paging3 Library ในการเรียกข้อมูลผ่าน Network API เพื่อแสดงผลในแอปกันครับ (ณ ปัจจุบันของบทความนี้ Paging3 Library ยังอยู่ในสถานะ Alpha 07)

Setup Paging3 Library

ก่อนอื่นทำการติดตั้ง Paging3 Library ในโปรเจกของเราใน gradle ดังนี้

ตัวอย่างแอปของเรา จะเป็นแอปทดสอบเรียกใช้งานข้อมูล API จาก Github Service โดยเราทำการพิมพ์ค้นหา repository ตามชื่อที่เราต้องการ จากนั้นแอปจะแสดงผล repository ที่ตรงกับคำค้นหานั้นมาให้

Setup API Service

สร้าง interface ที่เราจะเรียก API จาก Github Service โดยตัว API รองรับ 3 paramer ดังนี้
- q คือ query คำค้นหาที่เราต้องการ
- page คือ ระบุข้อมูลที่ต้องการอยู่ใน page ที่เท่าไร
- per_page คือ ต้องการข้อมูลที่ return กลับมาต่อ page มีจำนวนเท่าไร

สร้าง PagingSource

ให้ทำการสร้าง PagingSource ที่จะเป็นตัวจัดการสำหรับโหลดข้อมูลจาก API ให้เรา โดยจะทำการโหลดข้อมูลทีละส่วนเล็ก ๆ ไปแสดงผลในลิสต์ ตัวอย่างโค้ด ดังนี้

จากโค้ดจะทำการเรียกข้อมูล API จาก Github Service โดยเราส่ง query คำค้นที่ต้องการ พร้อมกับระบุ position และจำนวน size ข้อมูลที่ต้องการ เมื่อได้ response กลับมา ก็ทำการ return เป็น LoadResult.Page รูปแบบการทำงานใน function load อธิบายคร่าว ๆ ได้ ดังนี้

1. กำหนด position หากเรียกครั้งแรก params.key จะเป็น null ให้ใช้ตำแหน่ง default เป็น 1 และเมื่อมีการเรียก function load ครั้งถัดไป params.key จะนำค่าจาก nextKey มาใช้ให้อัตโนมัติ

2. ทำการเรียก API เมื่อได้ response กลับมาเป็น list ของข้อมูล จากนั้นทำการสร้าง instance เพื่อ return ค่าLoadResult.Page กลับไป พร้อมกับระบุ argument 3 อย่างคือ data, prevKey, nextKey

  • data คือ เป็นค่า response ที่ได้จากการเรียก API สมมุติเรากำหนด dataSource ว่าให้ extend<Int, Repo> ตัว data ที่เราต้องส่งกลับไปค่านี้จะต้องเป็น list ของ Repo (response: List<Repo>)
  • prevKey คือ ระบุว่า key ที่แล้วคืออะไร หากโหลดครั้งแรก prevKey จะเป็น null และหากโหลดครั้งถัดไป prevKey จะเท่ากับ prevKey = position - 1
  • nextKey คือ ระบุว่า key ถัดไปที่จะใช้ในการเรียก API คืออะไร โค้ดตรงนี้จะเช็ก ว่าหากข้อมูลที่ได้ response กลับมาเป็น null คือ ไม่มีข้อมูลแล้ว ให้กำหนด nextKey เป็น null ตัว data source จะรู้อัตโนมัติว่าไม่มีข้อมูลต้องโหลดต่อแล้ว และหากมีข้อมูลอีกก็ทำการ เพิ่มค่าให้ nextKey = position +1

3. ในขั้นตอนสุดท้าย หากมี error ให้ทำการสร้าง instance เพื่อ returnLoadResult.Error กลับไปพร้อมกับ exception

ตัว LoadResultเป็น sealed class ที่สามารถ wrap ข้อมูลส่งกลับไปในรูปแบบ ดังนี้

  • ถ้าเรียกใช้งาน API สำเร็จ จะทำการ return LoadResult.page กลับไปใน function load()คือ เป็นตัวบอกให้รู้ว่าข้อมูล data ในลิสต์ คืออะไร แล้วมี nextKey สำหรับดึงข้อมูลถัดไป และ prevKey ของข้อมูลที่แล้ว คือ อะไรนั่นเอง
  • ถ้ามี errror เกิดขึ้นเมื่อเรียก API ให้ return LoadResult.Error กลับไปใน function load()

ขั้นตอนถัดไปเราจะเรียกใช้ GithubPagingSource เพื่อนำข้อมูลที่ได้ไปแสดงผลในแอปของเรา

สร้าง GithubRepository

หลังจากเราสร้าง GithubPagingSource ซึ่งเป็น data source สำหรับใช้ใน paging เรียบร้อยแล้ว ต่อไปทำการสร้าง GithubRepository โดยมีรูปแบบโค้ด ดังนี้

  1. สร้าง reference ของ GithubService ที่ทำหน้าที่โหลดข้อมูลจาก API เพื่อส่งไปให้ GithubPagingSource
  2. ทำการ return instance ของ ClassPager ที่ทำหน้าที่โหลด stream ของข้อมูลจาก data sourceในบทความนี้ใช้ Flow จาก Kotlin coroutines (Paing รองรับการสร้าง Pager ในรูปแบบ Flow, LiveData, RXJava)
  3. ทำการสร้าง Pager โดยส่งค่า 2 Parameter ดังนี้
  • config ทำการสร้าง Class PagingConfigระบุจำนวนของpageSize ที่ต้องการ และplablePlaceHolders ว่าต้องการจะแสดง place holder หรือไม่ ในบทความนี้ไม่แสดง เลือกกำหนดเป็น false
  • pagingSourceRepositoryโดยเป็น lambda function ที่ return ค่าเป็น pagingSource ในกรณีนี้ เราทำการสร้าง class GithubPagingSource ที่ส่ง paramer คือ githubService และ query เข้าไป

สร้าง UseCase สำหรับเรียกข้อมูลจาก repository

ขั้นตอนต่อไปทำการสร้าง class GetGithubPagingourceUseCase เพื่อที่จะไปเรียกข้อมูลจาก GithubRepository และต่อไปใน view model จะทำการเรียก usecase นี้ เพื่อดึงข้อมูลจาก gitgub API มาแสดง

สร้าง View Model เพื่อส่งข้อมูลไปแสดงผลใน UI

ทำการสร้าง class MainViewModel เมื่อ user ป้อนคำค้นหา repository ที่ต้องการ จะไปเรียก function searchRepos(queryString: String) ที่ function นี้ก็จะไปเรียก GetGithubPagingourceUseCase โดยส่งคำค้นหา (query) เข้าไป และทำการตรวจสอบว่าข้อความที่ค้นหา เป็นข้อความเดิมหรือไม่ และเมื่อเป็นคำค้นใหม่ก็จะไปเรียก usecase และส่งข้อมูลกลับไปให้ UI แสดงผลในรูปแบบ Flow ของ PagingData

ใช้งาน PagingData Adapter

สร้าง adapter ที่ extend PagingDataAdapter<model,ViewHolder> ที่ค่าแรกจะเป็น model ที่เราจะใช้ ซึ่งเป็น model ตัวเดียวกันที่เราสร้างไว้ใน GithubPagingSource แล้ว ใน parameter ตัวที่สองก็ คือ ViewHolder ของ RecyclerView.Adapter

ต่อไปสร้าง diffUtil กำหนด logic ที่จะใช้ในการอัพเดตข้อมูลในลิสต์ ตรงนี้จะทำหน้าที่ตรวจสอบว่ามีข้อมูลใหม่เพิ่มเข้ามา หรือลบออกไป ก็จะไปเรียก notifyItemChanged และnotifyItemInserted ใน adapter ทำให้ข้อมูลที่แสดงผลในลิสต์อัพเดตตามให้เราอัตโนมัติ โดยที่เราไม่ต้องไปเขียน logic เพื่อเช็กเอง

แสดงผลข้อมูลใน UI

ขั้นตอนสุดท้าย คือ การแสดงผล ทำการ setup view สร้าง adapter ผูกกับ recyclerView ให้เรียบร้อย และส่งค่า query จาก editText ไปทำงานที่ function search() ตรงนี้จะไปเรียก function searchRepos() ของ view model อีกที ที่มีการ return ค่าเป็น Flowจากนั้นก็ใช้ collectLatest เพื่อรอรับค่า เมื่อได้ result กลับมา ก็ทำการอัพเดต adapter ด้วยการเรียก submitData จากนั้นก็เป็นอันเรียบร้อย ทดสอบรันดูการแสดงผลได้ครับ

สรุป

สำหรับบทความนี้ก็เป็นตัวอย่างเบื้องต้นแอปที่ทดลองใช้งานเบื้องต้นของ Paging3 Library ในการไปเรียกข้อมูลจาก API เพื่อมาแสดงผล และทำ load more ให้แบบอัตโนมัติ แต่ยังมีอีกหลายอย่างที่บทความนี้ยังไม่ครอบคลุม เช่น การดึงข้อมูลจาก API มาเก็บใน Room Database โดยใช้ remoteMediator หรือการเพิ่ม loading ในการแสดงผลตอน load more เป็นต้น ซึ่งหากสนใจเพิ่มเติมสามารถศึกษาได้จากที่มา หรือ google codelab ของ paging library ได้เลยครับ

--

--