บทความนี้ได้แปลงโค้ดจากภาษา 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