[Android] แสดงข้อมูลจาก Cache และ API ด้วย RxJava

บทความนี้ได้แปลงโค้ดจากภาษา Java ในบทความต้นทางเป็นภาษา Kotlin

Intro

หลายๆ ท่านที่เล่น Facebook App คงจะเคยเผลอเข้า App โดยที่ไม่ได้เชื่อมต่อ Internet หรือ Network มีปัญหาแต่ก็ยังสามารถเห็นข้อมูลบางส่วนใน App ได้

ถ้าคิดในแง่ของผู้ใช้งานก็คงจะน่าประทับใจมิใช่น้อย และในฐานะที่เราเป็นนักพัฒนาก็คงอยากจะให้ Application ของเรามี Feature ที่น่าจะประทับใจเช่นนี้เหมือนกัน

ดังนั้น ผมจึงได้ลองหาข้อมูลและได้พบบทความนึงของคุณ Miquel Beltran เรื่อง Caching With Realm and RxJava พบว่าค่อนข้างตรงกับที่ความต้องการเลยหยิบเอามาเขียนเล็กน้อย

Conceptual

1. ข้อมูลจาก API เท่านั้น

โดยทั่วไปแล้วเราจะแสดงข้อมูลที่ได้จาก API เพียงเท่านั้น ดังภาพ
ข้อมูลจาก API เท่านั้น

2. ข้อมูลจาก API และ Cache

เมื่อมีส่วนของการ Cache(ในที่นี้ใช้ Realm) จะแบ่งการทำงานเป็น 2 ส่วน คือ

2.1 เมื่อยัง ไม่มี ข้อมูลใน Cache จะทำการบันทึกข้อมูลจาก API ลง Cache ก่อนแล้วค่อย Query ข้อมูลจาก Cache เผื่อแสดงผล ดังรูป
ไม่มี ข้อมูลใน Cache จะทำการบันทึกข้อมูลจาก API ลง Cache ก่อน


2.2 เมื่อ มี ข้อมูลใน Cache ก็จะทำการ Query ข้อมูลจาก Cache ให้ View เพื่อแสดงผลก่อน หลังจากนั้นถึงจะเรียก API เพื่อที่จะให้ได้ข้อมูลที่สดใหม่ยิ่งกว่า ดังรูป
มี ข้อมูลใน Cache ก็จะทำการ Query ข้อมูลจาก Cache ก่อน

มาถึงตรงนี้ก็คงจะพอเห็นภาพกันบ้างแล้วว่า Application สามารถแสดงผลได้ถึงแม้ว่าจะไม่ได้เชื่อมต่อ Internet ก็ตาม แจ่มไปเลย :)

นอกจากนี้ข้อดีอีกอย่างก็คือ Application จะแสดงข้อมูลให้ผู้ใช้ได้ทันที ในขณะที่รอข้อมูลจาก API อยู่นั่นเอง

Let’s coding

ในโปรเจคนี้จะใช้ Library อยู่ 3 ตัว ได้แก่
และทำการเรียก API ไปที่ Open Weather Map
*** สำหรับท่านที่ตามเรื่อง RxJava ไม่ทัน ขอแนะนำให้ไปอ่านซีรีย์บทความเรื่อง RxJava ของคุณเอกที่ [Android Code] มารู้จักกับ RxJava และ RxAndroid กันเถอะ ก่อนนะครับ

เริ่มที่ Retrofit กับ RxJava

สร้าง WeatherService ด้วย Retrofit และ RxJava เพื่อใช้ในการเรียกไปยัง API

มาดูฝั่ง Realm กันบ้าง

ถ้าย้อนกลับขึ้นไปดูภาพด้านบน ก็จะเห็นได้ว่าเราต้องมีการ เขียนข้อมูล และ อ่านข้อมูล จาก Relam

ก่อนอื่นต้องสร้าง RealmObject ชื่อว่า WeatherRealm ดังนี้
มาดูคำสั่งในการเขียนข้อมูลลง Realm กันต่อ
สุดท้ายเป็นส่วนของคำสั่งในการอ่านข้อมูลจาก Realm นั้นสั้นๆ ดังนี้

เรียกใช้งานและ Chain คำสั่งทั้งหมด

ได้เวลาแสดงความเทพของ RxJava กันแล้ว
ลอง Run ดูก็ได้ผลลัพธ์ ดังนี้


จากโค้ดด้านบนเมื่อ subscribe จะมีการเรียก onNext() 2 ครั้ง โดยครั้งแรกจะมาจาก Observable ที่ทำงานเร็วที่สุด ในที่นี้ก็คือ Cache และอีกครั้งก็จะมาจาก API นั่นเอง

จะเห็นได้ว่าข้อมูลจะแสดงให้ผู้ใช้ได้ทันที ในขณะที่รอข้อมูลจาก API อยู่

นอกจากนี้ถึงแม้ว่าจะไม่ได้เชื่อมต่อ Internet ผู้ใช้ก็ยังจะสามารถเห็นข้อมูลจาก Cache ได้

Improvement

โดยทั่วไปแล้วเมื่อเข้า onNext() เราก็มักจะแสดงข้อมูลที่ได้ลงใน View เนื่องจากทำงานอยู่บน Main Thread (UI Thread) แล้ว เช่น
จะเห็นได้ว่าหากข้อมูลมาจากทั้ง Cache และ API จะต้องมีการ Render View ถึง 2 ครั้งด้วยกัน ตรงนี้ถ้าลดได้ ละได้ก็ควรทำเนาะ

แล้วจะลดการ Render View ได้ยังไง?

พระเอกของเราก็ คือ Observable.distinct() โดยคำสั่งนี้จะทำให้ Observable เรียก onNext() ก็ต่อเมื่อข้อมูลนั้นยังไม่ถูกเรียก (ถ้าข้อมูลซ้ำจะไม่มีการเรียก onNext() นั่นเอง) ดังภาพ
Observable.distinct()

และเพียงเพิ่มโค้ดเข้าไปอีกบรรทัดเดียวก็จะแก้ปัญหานี้ได้โดยพลัน …

เพียงเท่านี้เมื่อข้อมูลไม่มีการเปลี่ยนแปลง View ก็จะ Render เพียงครั้งเดียวเท่านั้น

แต่ถ้าหากข้อมูลมีการเปลี่ยนแปลง View ก็ Render ด้วยข้อมูลจาก Cache เพื่อให้ผู้ใช้ได้เห็นก่อน แล้วหลังจากที่ API ตอบกลับมาก็จะทำการ Render ข้อมูลที่ใหม่กว่าอีกรอบนั่นเอง

Thread

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

ซึ่งความตั้งใจของเจ้าของบทความ ต้องการให้การทำงานในแต่ละขั้นตอนมีการใช้งาน Thread ที่เหมาะสม ดังคำกล่าวที่ว่า
Put the right man To the right job
ใช้งาน Thread ให้เหมาะสมกับงาน
อธิบายได้ ดังนี้
  • การเรียกไปยัง API โดยปกติการเรียกไปยัง API เราจะให้ทำงานอยู่ที่ Background Thread (IO Thread) เพื่อหลีกเลี่ยงให้ Application ของเราหยุดการทำงานชั่วขณะ 
  • การแสดงผล ของ Android Application เมื่อจะต้องทำงานเกี่ยวกับ View จะต้องทำงานอยู่บน Main Thread (UI Thread) เสมอ 
  • การเขียนข้อมูลลง Realm ให้เขียนข้อมูลอยู่บน Computation Thread

Source Code

  • Kotlin : https://github.com/minibugdev/android-rxjava-realm-cache
  • Java : https://github.com/miquelbeltran/android-rxjava-realm-cache

Reference

Teeranai P

Developer ตัวน้อยๆ ที่หลงใหลในโลกของการพัฒนา Software. รักการเขียนโปรแกรมเป็นอันดับ 2 รองลงมาจากการนอน