วิธีเปลี่ยนกระบวนท่า จาก Retrofit ไป Fuel
โปรเจกเดิมเป็น Retrofit โปรเจกใหม่ใช้ Fuel โดยคนในทีม พอเราใช้เลยงงเลยทีนี้ อะแง มันจะยากไหมน๊า
สำหรับ Kotlin นั้น แน่นอนชาว Kotlin Fan ต้องแนะนำเจ้า Fuel แน่นอน
เจ้า Fuel คืออะไรหล่ะ?
คือ library ตัวนึง ที่ทำงานกับเจ้า RESTFul จาก API ว่าง่ายๆคือ library ที่ติดต่อกับ API นั่นเอง ซึ่ง library ที่ชาว Android Developer รู้จักกันดีก็คือ เจ้า Retrofit นั่นเอง
ส่วน feature นั้น แนะนำให้จิ้มอ่านลิ้งนี้เลย คร่าวๆก็เหมือนเจ้า Retrofit คือ สามารถเชื่อมต่อกับ API เช่น ดึง/ส่งข้อมูลระหว่าง API กับแอป, upload/download file แล้วที่เด่นๆเตะตาแตก คือ RxJava 2.x, ใช้ร่วมกันกับ LiveData ใน Android Architecture Component, Coroutines ใน Kotlin อีกด้วย
แล้วใช้ยังไงหล่ะ? วิธีใช้ขั้นต้นลองไปอ่านใน repo github ด้านบนนี้นะ ในที่นี้เรามาเล่นท่ายาก แบบ advance ไปเลย นั่นคือ Router design pattern นั่นเองงงง
เราจะมาพูดถึงการเขียนการทำงานกับฝั่ง backend กับ app ว่าถ้าใช้ท่าของ Retrofit จะเปลี่ยนมาใช้ท่าของ Fuel ได้อย่างไร เราลองวาดๆดูน่าจะคร่าวๆประมาณนี้
สิ่งที่ต่างไป คือ การคุยกับเจ้า API นั่นเอง โดยฝั่ง Retrofit จะสร้าง gateway มาตัวนึง และฝั่งเชื้อเพลิง เอ้ยย Fuel นั้น ใช้ routing ในการเรียก
กระบวนท่าของ Fuel แบบ Retrofit
รายละเอียดภายในตัวโปรเจกคร่าวๆ โปรเจกเดิมเป็น Java ที่ใช้กับ Retrofit ส่วนโปรเจกล่าสุดเป็น Kotlin ที่ใช้ร่วมกันกับ Fuel โดยตัวอย่างเป็นการเรียกตัว profile ของ user คนนั้นๆขึ้นมาแล้วกันเนอะ
ก่อนอื่นอย่าลืมเพิ่มเจ้า Fuel ใน build.gradle
ของ module: app ก่อนนะเออ
จากนั้นเพิ่ม class ของ model ก่อน ซึ่งใช้เจ้า Parcelable ร่วมด้วย ซึ่งขอไม่อธิบายละเอียดในที่นี้นะ แต่จับตามองเจ้า Deserializer นะ มีเฌอไพร์ส เอ้ยยย เซอไพร์ส (เล่นมุกไม่ปรึกษาใคร ขออภัยด้วยนะกั๊บบ)
ต่อมา สิ่งที่ต่างกันระหว่างเจ้า Retrofit กับ Fuel นั่นคือ ตอนที่คุยกับ API นั่นเอง ขอ Retrofit เราจะสร้างเป็น gateway เพื่อคุยกับ API
@Header("X_API_KEY")
@POST("<some_sub_url>")
Call<Response> gatewayFunction(@Body Request request)
ส่วนเจ้า Fuel อาจจะแปลกๆสักนิด มาทำความรู้จักและเปรียบเทียบกันดีกว่า
class ของเจ้า Routing จะใช้ Sealed Class เพราะว่าเราต้องการสร้าง sub-class ในนี้ เราก็บอกเขาว่าของสร้าง sub-class เฉพาะในไฟล์นี้เท่านั้นนะ
และตรง basePath ก็คือ path ตั้งต้นที่ไปคุยกับ API นั่นแหละ สมมุติว่าในนี้เป็นตัวแปร constant ที่เป็น String ตัวนึงก็แล้วกันเนอะ
sealed class ProfileRouting: FuelRouting {
val PARAM_AUTH = "Authorization"
override val basePath: String
get() = Constant.BASE_PATH
class getProfile(val token: String): ProfileRouting() {
override val method: Method
get() = Method.GET
override val path: String
get() = "/users"
override val params: List<Pair<String, Any?>>?
get() = null
override val headers: Map<String, String>?
get() = null
}
}
เราสร้าง sub-class ขึ้นมาใหม่ตัวนึง ให้เจ้านี่ทำงานโดยการไป get profile นั้นๆมา โดยไส้ในเป็นดังนี้
- method : หลักๆก็ GET, POST, PUT อะไรพวกนี้อ่ะ เวลาใช้ก็ Method.<method> เช่น
Method.GET
- path : อันนี้ใส่เจ้า sub-path ลงไป เป็น String เนอะ ถ้าไม่มี sub-path ก็ใส่เป็น “” ไป
- params : อันนี้เหมือนเป็นโยนเจ้า Request ลงไป ถ้าไม่มีก็ null ถ้ามีก็ใส่เป็น
listOf()
เช่นlistOf(“Token” to token, “Name” to name)
- headers : ก็ header นั่นแหละ จะอะไรเล่าาาา อันนี้คล้ายๆกับ params ไม่มีก็ null ถ้ามีก็ใส่เป็น
mapOf()
เช่นmapOf(“Token” to token, “Name” to name)
ตอนแรกๆอาจจะไล่โค้ดตามงงๆหน่อย แต่เข้าใจแล้วก็เข้าใจเลยเนอะ
ขั้นสุดท้ายล้าวววว ในส่วนของ presenter ซึ่งมีกระบวนท่าที่ต่างกันด้วยนะ พอเอามาเทียบดูพบว่าฝั่ง Retrofit โค้ดยาวกว่า และต้อง handle case เพิ่มหลายส่วนเลยแหละ เพิ่มจาก response.isSuccessful()
เช่น ตัว response.body ต้องไม่ null, มีความยาวมากกว่า 0 แบบนี้
ส่วนเจ้า Fuel ไม่ต้อง handle อะไรยุบยับแบบนี้ มีแค่ success กับ fail เท่านั้น
ในขั้นตอนนี้เราจะต้องสร้าง listener มาตัวนึง เพื่อนำมาเรียกใช้หลังจาก call API ได้ success หรือ fail ว่าหลังจากนั้น จะให้ทำอะไรต่อ เช่น ถ้า success ก็นำ data ที่ได้ไปแสดงผล หรือถ้า fail ก็แสดงผลว่าไม่สามารถโหลดข้อมูลขึ้นมาได้ เป็นต้น
การสร้างก็ไม่มีอะไรมากเลยจริงๆ อันนี้ทำเหมือนกัน ทั้ง Retrofit และ Fuel เนอะ
interface ProfileListener {
fun onProfileSuccess(profile: Profile)
fun onProfileFailure(error: FuelError)
}
หลังจากทำเสร็จแล้ว ต้องนำไปใช้สิจ๊ะ วิธีการใช้เหมือนกัน คือ เรียก function ที่เราสร้างจากเจ้า presenter ซึ่งท่าเหมือนกันทั้งใน Java และ Kotlin แต่ต่าง syntax กัน
ProfilePresenter(this).getUserProfile()
แน่นอน การเรียกใช้แบบนี้ จะต้อง implement เจ้า listener ด้วย เพื่อนำไป handle ต่อในกรณี success หรือ fail ตามที่กล่าวไว้เนอะ
override fun onProfileSuccess(profile: UserProfile) {
//TODO: handle for success
}
override fun onProfileFailure(error: FuelError) {
//TODO: handle for success
}
จากนั้นลอง run ดูเนอะ อันนี้ตัวใครตัวมันหล่ะ ว่าแต่ละคนจะเขียน API เพื่อทำงานอะไรในแอปบ้างเนอะ ถ้า fail เราก็ให้มันพ่น error ออกมาแล้วเราเอาไปแก้ให้ถูกต้อง ที่ผิดอาจจะเป็นชื่อ model ไม่ตรง API, ใส่ Method ผิด, path ผิด, ใส่ data ไปให้มันผิด อันนี้ก็แล้วแต่เคราะห์กรรมที่เจอมา
สุดท้ายนี้ก็หวังว่าจะอ่านรู้เรื่องกัน ฮ่าๆ ตอนเราติดปัญหาคือไม่ค่อยมีใครเขียนบล็อกถึงเจ้า Fuel เลย ภาษาไทยเหรอ ไม่มี ได้แต่นั่งอ่านใน github และดูโค้ดที่น้องเขียนไว้ แก้เสร็จเลยลองมาเขียนบล็อกนี้ดู เผื่อเป็นประโยชน์เนอะ :D
สุดท้ายฝากร้านกันสักนิด ฝากเพจด้วยนะจ๊ะ