Dagger Android Injection - Part 2 - Eliminate Boilerplate code

Nutron
3 min readFeb 11, 2018

--

image power by www.flaticon.com

ในบทความที่แล้วเราได้พูดถึง การ Injection Activity ด้วยวิธีการใหม่ใน Dagger version 2.11 กันไปแล้ว ในบทความนี้เราจะมาต่อด้วยการทำให้โค้ดของบทความที่แล้วสั้นและกระชับขึ้น ซึ่งถ้าใครยังไม่ได้อ่าน แนะนำว่าควรจะไปอ่านก่อนครับ โดยบทความนี้เป็นส่วนหนึ่งของ 3 หัวข้อที่เราจะพูดถึง

ในบทความนี้จะกล่าวถึงการปรับปรุง class Application และพูดถึงการใช้งาน Annotation พิเศษของ Dagger ที่จะช่วยให้การสร้าง Component ของเราง่ายขึ้น

Improve MainApplication

ในบทความที่แล้วขั้นตอนใน step5 ที่ MainApplication ของเราต้อง implement HasActivityInjector เพื่อบอกให้ Dagger รู้ว่าเรามี Activity ที่ต้องการ Inject โดยหน้าตาของ MainApplication จากบทความที่แล้วเป็นดังนี้

ซึ่งขั้นตอนนี้ หากเราไม่ต้องการ implement HasActivityInjector และไม่ต้องการสร้างตัวแปล dispatchingAndroidInjection ขึ้นมาเอง เราสามารถเลี่ยงได้ด้วยการให้ MainApplication ของเรา implement DaggerApplication โดยหน้าตาใหม่ของ MainApplication จะเป็นดังนี้

จะเห็นว่าโค้ดสั้นลงอย่างเห็นได้ชัด แต่อย่างไรก็ตามเรายังคงต้องบอก Dagger ว่า AndroidInjector ของเราจะเป็นตัวไหน ซึ่งในที่นี้เราจะต้องไปแก้ class AppComponent ของเราให้ Implement AndroidInjector<DaggerApplication> เนื่องจากเราจะคืนค่า AppComponent กลับไปให้กับ Dagger แทนการคืนค่า dispatchingAndroidInjection โดยหน้าตาของ AppComponent ใหม่จะเป็นดังนี้

แต่อย่างไรก็ตาม โดยส่วนตัวแล้วผมชอบวิธี implement HasActivityInjector โดยตรงมากกว่า ถึงแม้โค้ดจะเยอะกว่าแต่มันก็ชัดเจนกว่า ว่าเรากำลังทำอะไร และยังช่วยให้ไม่ไปกระทบกับ Class อื่นๆอย่างเช่น AppComponent เป็นต้น

อีกสิ่งหนึ่งที่ทำให้เลือกวิธีนี้คือการยึดหลัก “Composition over inheritance” นั่นหมายความว่า หากเราต้องการเพิ่มความสามารถให้กับ class Application ของเรา การ implement (compose) เป็นวิธีที่เหมาะกว่า นอกจากจะไม่เป็นการผูกมัดกับ class DaggerApplication มากเกินไปแล้ว ยังมีประโยชน์ตรงที่หากวันใดที่ class Application ของเราจำเป็นต้อง extend class อื่นๆ เช่น MultidexApplication เราก็สามารถทำได้โดยไม่กระทบกับการทำงานเดิม

Eliminate boilerplate with new Annotation

หากลองจินตนาการว่าเรามี component สัก 50 components สิ่งที่จะเกิดขึ้นคือ โค้ดของการสร้าง Component จะถูกทำเหมือนเดิมซ้ำๆ (ดู step2 ของบทความที่แล้วประกอบ) โดยแทบจะก็อปโค้ดแป่ะกันเลยทีเดียว มีแค่การสร้าง Module เท่านั้นที่ต่างกัน

นอกจากนี้เมื่อ Activity เยอะขึ้น ก็ต้องสร้าง ActivityComponent เยอะขึ้นตาม ซึ่งเราก็ต้องค่อยบอก Dagger ว่า จะมี ActivityComponent อะไรบ้าง โดยเพิ่มโค้ดลงใน class ActivityBinder (อยู่ใน step3 ของบทความที่แล้ว) ซึ่งเจ้า ActivityBinder ก็จะใหญ่ขึ้นเรื่อยๆ ดังนั้นแทนที่เราจะสร้าง ActivityComponent เองทุกครั้ง เราจะให้ Dagger เป็นคนสร้างให้เราแทน โดยใช้ Annotation ใหม่ที่ชื่อว่า @ContributesAndroidInjector โดยเรามีหน้าที่แค่สร้าง Module ให้กับ ActivityComponent เหล่านั้นก็พอ ดังนั้นความสัมพันธ์หลังจากใช้ Annotation จะได้หน้าตา Diagram ใหม่ตามแผนภาพดังนี้

จะเห็นว่า MainActivityComponent หายไป เหลือไว้แต่ Module ของมันเท่านั้น โดยขั้นตอนการใช้งาน @ContributesAndroidInjector สามารถทำได้ดังนี้ครับ

EB1: เพิ่ม library ใหม่ใน build.gradle

ขั้นแรกให้เราเพิ่มคำสั่งด้านล่างใน dependencies ที่อยู่ใน build.gradle โดยในที่นี้เราจะใช้ Dagger version 2.13 กันครับ

kapt "com.google.dagger:dagger-android-processor:$daggerVersion"

EB2: Update Class ActivityBinder

ต่อไปให้เราทำการแก้ไข class ActivityBinder ของเราโดยให้เปลี่ยนไปใช้ @ContributesAndroidInjector แทน โดยหน้าตาใหม่ของ ActivityBinder จะเป็นดังนี้

หากสังเกตดีๆเราจะพบว่า เราจะระบุ MainActivityModule ให้กับ @ContributesAndroidInjector โดยตรงแทนที่จะระบุใน MainActivityComponent (ซึ่งจะไม่มีแล้ว) และเราจะคืนค่าMainActivity ออกไป แทนที่จะเป็น AndroidInjector.Factory<out Activity> แบบเดิม

EB3: ลบ Class MainActivityComponent ทิ้ง

จากที่เกริ่นไปตั้งแต่ตอนต้นว่า annotation @ContributesAndroidInjector จะไปสร้าง Component ให้เราเองอัตโนมัติ ทำให้เราไม่จำเป็นต้องมี class MainActivityComponent อีก และนี่จึงเป็นสาเหตุว่าทำไมในขั้นตอน EP2 เราจึงระบุ Module ให้กับ @ContributesAndroidInjector ทั้งนี้ก็เพื่อให้มันนำไปใช้ในการสร้าง Component ให้กับเรานั่นเอง

Note* เห็นไหมว่าทุกครั้งที่เราสร้าง component เราจะกำหนด Moduleให้มันเสมอ เพราะ Component คือสะพานที่เชื่อระหว่าง Module และการทำ Injections ซึ่งนั่นเป็นสาเหตุว่าทำไม เมื่อเราใช้ @ContributesAndroidInjector เราจึงต้องกำหนด Module ให้มัน เพราะว่ามันจะไปสร้าง Component ให้เรานั้นเอง

EP4: ลบคำสั่ง subComponent ใน AppModule

ข้อดีอีกอย่างหนึ่งของการใช้ @ContributesAndroidInjector คือเราไม่จำเป็นต้องระบุ MainActivityComponent ในคำสั่ง subcomponents ที่อยู่ใน AppModule อีกแล้ว เพราะ Dagger จะจัดการให้เราเอง ดังนั้นเราสามารถลบมันออกได้ หน้าตาก็จะประมาณนี้

แค่นี้ก็เป็นอันเสร็จเรียบร้อย ซึ่งหากใครสนใจดูโค้ดทั้งหมดสามารถไป Checkout ออกมาดูได้ตาม link ด้านล่างนี้ได้ครับ

Conclusion

เราจะเห็นว่าการใช้งาน Class ที่ Dagger provide มาให้อย่าง DaggerApplication หรือ @ContributesAndroidInjector จะช่วยลดโค้ดที่เราต้องเขียนไปได้เยอะมากทำให้ความซับซ้อนของโค้ดน้อยลง โค้ดดูอ่านง่ายขึ้น แต่อย่างไรก็ตามก่อนที่เราจะใช้งานมันก็ควรจะเข้าใจมันก่อนว่า มันมีที่มาที่ไปอย่างไร และเหมาะสมหรือไม่ที่จะใช้เครื่องมือเหล่านั้น

ในบทความหน้า เราจะพูดถึงการ Inject Fragment กันบ้าง เราลองมาดูกันสิว่า มีความแตกต่างจากการ Inject Activity มากน้อยแค่ไหน รอติดตามกันนะครับ

สุดท้ายนี้ก็เช่นเคยครับ หากผู้อ่านคิดว่าบทความนี้มีประโยชน์ ก็ฝากกด 👏 เป็นกำลังใจให้ด้วยนะครับ

--

--