[Android] สถาปัตยกรรมของแอนดรอยด์ ตอนที่ 3 : View Model

Jedsada Tiwongvorakul
20Scoops CNX
Published in
3 min readJun 21, 2017

View Model คือ คลาสที่ถูกออกแบบมาเพื่อทำการเก็บ และจัดการข้อมูลที่นำไปแสดงบน UI ให้มีชีวิตรอดปลอดภัยจากการเกิด Configuration changes เช่นการหมุนจอ เนื่องจากว่าการหมุนจอนั้นทำให้ Activity ถูกทำลาย และค่าในตัวแปรก็จะหายสาบสูญไปในอากาศ

ถ้าหาก Activity มีการเชื่อมต่อกับ Web Service ในครั้งแรกที่เปิดขึ้นมา และผู้ใช้รอจนโหลดเสร็จก็คงไม่มีปัญหาอะไร แต่หากถ้าว่าระหว่างที่ทำการโหลดข้อมูลอยู่นั้น
ผู้ใช้ได้ทำการหมุนหน้าจอล่ะ (Configuration changes) ปกติเจ้าของบล็อคจะทำการเช็คที่ onDestroy() ว่ามีการเรียก APIs หรือไม่ ถ้ามีก็จะทำการยกเลิก(มันเป็นเรื่องของ Lifecycle สามารถอ่านได้ที่นี่) และถ้าเกิดกรณีที่เลวร้ายเอามากๆ เช่น ผู้ใช้หมุนไปเรื่อยๆ ผลลัพธ์ก็ออกมาประมาณนี้

  • กรณีที่ผู้ใช้ไม่ได้หมุนจอ
  • กรณีที่ผู้ใช้หมุนจอแบบบ้าระห่ำ

จะเห็นได้ว่าถ้าผู้ใช้ขยันหมุนจอไปเรื่อยๆ แบบนี้ผู้ใช้ก็จะไม่มีวันได้เห็นข้อมูลที่กำลังโหลดอยู่แน่นอน นั่นคือที่มาของ View Model เนื่องจาก Lifeycle ของ View Model นั้นยาวกว่า Activity/Fragment ยาวกว่ายังไงไปดูกัน

https://developer.android.com/images/topic/libraries/architecture/viewmodel-lifecycle.png

จากรูป View Model จะมี Lifecycle ที่ยาวกว่า Activity/Fragment เพราะจากรูป onDestroy() คือ state สุดท้ายของ Activity/Fragment หมายความว่าเมื่อ Activity/Fragment ถึงแก่ความตายแต่ View Model ไม่ตายนะ U

มาถึงจุดนี้ก็พอจะมองเห็นภาพการทำงานของ View Model กันคร่าวๆกันบางละ
และถ้ามีข้อสงสัยว่า ViewModel ที่เจ้าของบล็อคกำลังพูดถึงนั้นมันใช่คำที่อยู่ใน MVVM ไหม? จากบทความของพี่ Ake Exorcist และจากที่เจ้าของบล็อคได้ไปแอบดูโค้ดของชาวบ้านก็ได้คำตอบว่า “ใช่ครับ”

และในส่วนของการเขียนแบบ MVVM นั้นก็ไปหาดูความหมายกันเองละกันนะครับ เอาล่ะมาดูในส่วนของโค้ดการใช้งาน View Model กันเลยดีกว่า

โดยโจทย์ของเราในวันนี้ก็คือทำการเชื่อมต่อกับ APIs ของ Github เพื่อทำการดึงข้อมูลของผู้ใช้ และในตัวอย่างจะทำการแสดงคือชื่อโดยในส่วนของการเชื่อมต่อกับ Web Service เจ้าของบล็อคใช้ Retrofit + RxJava2 และภาษาที่ใช้เขียนจะเป็น Kotlin นะครับ (แอบเขียน Kotlin แรดมาก 555)

ขั้นตอนที่ 1 : ทำการเพิ่ม Dependency ของ Lifecycle Architecture Component เข้ามาในโปรเจค ดังนี้

compile 'android.arch.lifecycle:extensions:1.0.0-alpha2'
annotationProcessor 'android.arch.lifecycle:compiler:1.0.0-alpha2'

ขั้นตอนที่ 2 : ต่อมาก็มาทำการสร้าง Layout เพื่อที่จะแสดงข้อมูลผู้ใช้ โดยเจ้าของบล็อคขอทำแบบไม่หรูหรามากนักนะครับ จะได้ดังนี้

เนื่องจากเราจะเขียนเป็นแบบ MVVM แล้วเจ้าของบล็อคเลยขอนำเอาเรื่องของการทำ Data Binding มาใช้คู่กันเลยนะครับ

สำหรับการทำ Data Binding ใน Kotlin นั้นต้องทำการเพิ่ม Dependency และทำการตั้งค่า ในไฟล์ build.app (app) ดังนี้

dependencies {
...
kapt 'com.android.databinding:compiler:2.3.3'
}

kapt {
generateStubs = true
}

ขั้นตอนที่ 3 : ทำการสร้างคลาส Model (POJO) เพื่อทำจะทำการ Convert จากข้อมูลที่ Web Service ที่เป็น JSON โดยถ้าเป็น JAVA จะเป็นคลาสที่มี setter/getter ของตัวแปรอยู่ แต่เมื่อเป็น Kotlin ก็จะเป็น data class และไม่จำเป็นต้องมี setter/getter ดังนี้

ขั้นตอนที่ 4 : ทำการสร้าง interface ของ UserInfoApi สำหรับดึงข้อมูลผู้ใช้จาก Github ดังนี้

ขั้นตอนที่ 5 : ก็ทำการสร้างคลาส MainActivityViewModel.kt ขึ้นมาที่สืบทอดมาจากคลาส ViewModel ก็จะได้ดังนี้

จากโค้ดตัวอย่างแน่นอนว่าจะต้องดึงความสามรถของเจ้า Live Data มาใช้ควบคู่กันไปด้วย และขอบอกการที่ทำการสร้างตัว Rrtrofit ไว้ในคลาสนี้ไม่ใช่วิธีที่ถูกต้องนะครับ ควรจะทำแยกออกไปเป็นอีก Model หนึ่ง และใช้ Framework ที่ช่วยในการจัดการเรื่องของ Dependency Injection (DI) เช่น Dagger 2 เข้ามาช่วย หรือโยน Model เข้าผ่านทาง Constructor ก็ได้นะครับ

ขั้นตอนที่ 6 : ทำการเรียกใช้งาน MainActivityViewModel.kt ที่ MainActivity.kt ก็จะออกมาประมาณนี้

จากโค้ดด้านบนบรรทัดที่ 5 เป็นเรื่องของ Data Binding นะครับ วิธีการใช้งาน MainActivityViewModel.kt คือต้องทำการเรียกผ่าน ViewModelProviders
และทำการสังเกตการของการเปลี่ยนแปลงข้อมูลจากการเรียกใช้ getUserInfo(…) ซึ่งเป็นการใช้งานของ Live Data และเมื่อข้อมูลมีการเปลี่ยนแปลงเจ้าของบล็อคก็ทำการซ่อน Progress bar และทำการกำหนดค่าให้กับตัวแปรที่ได้ทำการ Binding ไว้ เนื่องจากมันเป็นความสามารถของเจ้า Data Binding

ถ้าหากใน MainActivityViewModel.kt ของเจ้าของบล็อค ต้องการโยนบางอย่างเข้าไปใน Constructor ละ ซึ่งวิธีที่เจ้าของบล็อคใช้เป็นแบบธรรมดาไม่ได้เล่นท่าอะไรจึงไม่สามารถทำได้ วิธีแก้ไขคือ นำเอา Factory pattern เข้ามาช่วย

ขั้นตอนที่ 7 : ทำการกด Run แล้วดูผลลัพธ์กันเลยครับ ระหว่างที่โหลดก็ให้หมุนหน้าจอไปด้วยนะครับ เพื่อทดสอบว่าเมื่อเราใช้ ViewModel แล้วการเกิด Configuration changes ผลลัพธ์ก็จะได้ดังนี้

จะเห็นได้ว่าเมื่อมีการหมุนจอการ ส่วนของการเชื่อมต่อ Web Service ก็จะยังทำงานต่อ เพราะเจ้าของบล็อคได้ทำการเอาไว้ที่ View Model เพราะ มี Lifeycle ที่ควบคุมถึง onDestroy() จึงทำให้การทำงานอยู่รอดปลอดภัยนั่นเอง

สรุป

การมาของ View Model ซึ่งเป็น Component ใหม่ของ Android นั้นเข้ามาช่วยให้นักพัฒนาแอพแอนดรอย์หมดปัญหาในเรื่องของการรับมือเมื่อเกิด Configuration changes ระหว่างการทำงานได้สบายมากขึ้น และทำให้ไม่จำเป็นต้องใช้ method onSaveInstanceState() สุดท้ายมันส่งผลทำให้รูปแบบโครงสร้างโค้ดเป็นแบบ MVVM โดยอัตโนมัติ ถ้าใครสนใจที่จะใช้ View Model ก็อยากลืมไปศึกษาคอนเซ็ปของ MVVM ด้วยนะครับ

--

--