ในบทความที่แล้วเราได้พูดถึง การ Injection Activity ด้วยวิธีการใหม่ใน Dagger version 2.11 กันไปแล้ว ในบทความนี้เราจะมาต่อด้วยการทำให้โค้ดของบทความที่แล้วสั้นและกระชับขึ้น ซึ่งถ้าใครยังไม่ได้อ่าน แนะนำว่าควรจะไปอ่านก่อนครับ โดยบทความนี้เป็นส่วนหนึ่งของ 3 หัวข้อที่เราจะพูดถึง
- Part1 - Inject Activity with Dagger Android Injection
- Part2 - Eliminate Boilerplate code in Dagger Android Injection
- Part3 - Inject Fragment with Dagger Android Injection
ในบทความนี้จะกล่าวถึงการปรับปรุง 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
มากน้อยแค่ไหน รอติดตามกันนะครับ
สุดท้ายนี้ก็เช่นเคยครับ หากผู้อ่านคิดว่าบทความนี้มีประโยชน์ ก็ฝากกด 👏 เป็นกำลังใจให้ด้วยนะครับ