วิธีเปลี่ยนกระบวนท่า จาก Retrofit ไป Fuel

android May 09, 2018

โปรเจกเดิมเป็น Retrofit โปรเจกใหม่ใช้ Fuel โดยคนในทีม พอเราใช้เลยงงเลยทีนี้ อะแง มันจะยากไหมน๊า

I really enjoy driving all around Iceland; be careful to not be out of fuel:)
Photo by Mahkeo / Unsplash

สำหรับ 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 อีกด้วย

kittinunf/fuel
The easiest HTTP networking library for Kotlin/Android - kittinunf/fuel

แล้วใช้ยังไงหล่ะ? วิธีใช้ขั้นต้นลองไปอ่านใน 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 ก่อนนะเออ

implementation 'com.github.kittinunf.fuel:fuel-android:1.12.0'
implementation 'com.github.kittinunf.fuel:fuel-gson:1.12.0'
compare code กันให้เห็น แล้วจะอธิบายความต่างระหว่าง Java และ Kotlin ในบล็อกถัดๆไป

จากนั้นเพิ่ม 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


สุดท้ายฝากร้านกันสักนิด ฝากเพจด้วยนะจ๊ะ

อย่าลืมกด like กด share บทความกันด้วยนะคะ :)

Posted by MikkiPastel on Sunday, 10 December 2017

Tags

Minseo Chayabanjonglerd

Android Developer ผู้เป็นเจ้าของบล็อก MikkiPastel ที่ชอบทำหลายๆอย่างนอกจากเขียนแอพแอนดรอยด์ เช่น เขียนบล็อก เขียนแชทบอท เรียนออนไลน์ อ่านหนังสือ วาดรูปเล่น ดู netfilx สั่งอาหารอร่อยๆกัน เป็นต้น

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.