บทความนี้จะพูดถึงการทำ dependency Injection ด้วย Dagger2 โดยจะเน้นอธิบาย ถึง API ใหม่ใน Dagger2 version 2.11+ ที่สามารถทำงานร่วมกัน Android Environment ได้ดีทำให้การ Inject Activity หรือ Fragment ทำได้ง่ายขึ้น โดยจะแบ่งเป็นสองตอนคือ
- Part1 - Inject Activity with Dagger Android Injection
- Part2 - Eliminate Boilerplate code in Dagger Android Injection
- Part3 - Inject Fragment with Dagger Android Injection
Lessons learn
จากบทความที่แล้วเราได้ทราบว่า Activity กับ Application มีความสัมพันธ์กัน โดย Application จะประกอบไปด้วยหลายๆ Activity ดังนั้น AppComponent
ที่เป็น Component หลักของ Application จึงเป็น Root ของ ActivityComponent
อื่นๆด้วย เช่นกัน ซึ่งแสดงให้เห็นดังภาพด้านล่าง
ในทำนองเดียวกัน Fragment กับ Activity ก็มีความสัมพันธ์เช่นเดียวกับ Activity กับ Application โดยหนึ่ง Activity อาจจะประกอบไปด้วยหลายๆ Fragment (หรือไม่มีเลย) ซึ่งแน่นอนว่า Component ของ Fragment ก็ต้องมีความสัมพันธ์กับ Component ของ Activity ด้วย ถึงจุดนี้หลายๆคนคงน่าจะเดาออกว่าภาพรวมความสัมพันธ์ของ FragmentComponent
และ ActivityComponent
จะเป็นอย่างไร ใครที่ยังนึกไม่ออกลองดูภาพด้านล่างประกอบครับ
Start implementing, No more theory!
หลังจากที่เราเห็นภาพรวมของความสัมพันธ์กันไปแล้ว ก็ได้เวลาลงมือกันครับโดยจะขอทำต่อจากบทความที่แล้ว โดยสมมุติเหตุการณ์ว่า MainActivity
ของเรามี DetailFragment
เป็นส่วนหนึ่งของการแสดงผลข้อมูล ซึ่งก่อนที่จะไปเริ่มลงโค้ดกัน ก็ขอทบทวนขั้นตอนการ Inject Activity กันก่อน โดยขั้นตอนของการ Inject Activity สามารถสรุปคร่าวๆได้ดังนี้
- สร้าง
AppModule
ซึ่งเป็น Module ของAppComponent
ขึ้นมา - สร้าง
AppComponent
แล้วกำหนดAppModule
ให้กับAppComponent
- สร้าง Module ของ
ActivityComponent
- สร้าง
ActivityComponent
แล้วกำหนด Module ที่สร้างในข้อสามให้กับActivityComponent
- กำหนด
ActivityComponent
ให้กับAppModule
โดยใช้คำสั่งsubcomponent
- สร้าง
ActivityBinder
เพื่อบอก Dagger ว่าจะมี Activity Component อะไรบ้าง - กำหนด
ActivityBinder
ให้กับAppComponent
เนื่องจาก root component ของ ActivityComponent ต่างๆคือAppComponent
Implement
interface
HasActivityInjector
ใน classApplication
เพื่อกำหนดตัวActivityInjector
ให้กับ Dagger- สร้างตัวแปล
DispatchingAndroidInjector<Activity>
แล้วreturn
กลับไปใน methodactivityInjector
ที่มาพร้อมกับinterface
HasActivityInjector
เพื่อใช้ dagger สามารถ InjectActivity
ต่างๆของเราได้ - [OPTIONAL] ทำลายขั้นตอนที่ 4, 5 ด้วย
@ContributesAndroidInjector
เมื่อเข้าใจถึงวิธีการ Inject Activity แล้ว การ Inject Fragment ก็จะทำไม่ต่างกันครับ โดย FragmentComponent จะไปอิงกับ ActivityComponent แทน
Step 1). สร้าง Module ของ FragmentComponent
ให้เราสร้าง Module ของ Component ขึ้นมาก่อน โดยในที่นี้จะให้ชื่อว่า DetailFragmentModule ครับ
Step 2). สร้าง FragmentBinder
เนื่องจากว่าเราจะใช้ความสามารถของ @ContributesAndroidInjector
ดังนั้นเราจะข้ามขั้นตอนของการสร้าง Component สำหรับ Fragment ไป เพราะ@ContributesAndroidInjector
จะไปสร้าง Component ให้เราเองอัตโนมัติ ซึ่งหน้าตาความสัมพันธ์จะเป็นประมาณภาพด้านล่างโดยให้เราสังเกตุว่า เราจะไม่มีการสร้าง FragmentComponent
ขึ้นมา
ให้เราสร้าง class MainFragmentBinder
ขึ้นมา ที่ตั้งชื่อเป็น MainFragmentBinder
เพราะว่า class นี้จะเป็นตัวกำหนด FragmentComponent
ทั้งหมดที่จะมีใน MainComponent
ซึ่ง class FragmentBinder
จะเป็น class ที่บอก Dagger ให้รู้ว่าเราจะมี FragmentComponent
อะไรบ้าง โดยในที่นี้เราจะมีแค่ DetailFragmentComponent
step 3). กำหนด MainFragmentBinder ให้กับ MainActivityComponent
กำหนด MainFragmentBinder
ให้กับ MainActivityComponent
ที่อยู่ใน ActivityBinder
เนื่องจาก root component ของ DetailFragmentComponent
ก็คือ MainActivityComponent
ซึ่งจะเห็นว่าในขั้นตอนนี้จะคล้ายๆกับตอนที่เรากำหน่อย ActivityBinder
ให้กับ AppComponent
นั้นเอง
step 4). Implement interface HasSupportFragmentInjector ใน MainActivity
Implement interface HasSupportFragmentInjector
ให้กับ MainActivity
เพื่อบอก Dagger ว่าเรามี Fragment ที่ต้องการ Inject ใน MainActivity
นี้ และอย่าลืมสร้าง instance ของ DispatchingAndroidInjector
ให้กับ interface นี้ด้วย ซึ่งจะได้โค้ดหน้าตาประมาณนี้
ซึ่งในขั้นตอนนี้หากใครจะใช้วิธีลัดให้กระชับกว่าเดิม (เหมือนตอนใช้ DaggerApplication) ก็สามารถเปลี่ยนจากการ implement HasSupportFragmentInjector
ไปเป็นการ Extend
ความสามารถของ Class DaggerAppCompatActivity
ได้ เพราะ class นี้ Implement interface HasSupportFragmentInjector
ให้กับเราแล้ว แต่อย่างที่บอกเรายังยึด Concept “Composition over inheritance” ดังนั้น เราจะเลือกวิธี Implement ครับ
step 5). Inject Fragment
ถึงจุดนี้เราก็พร้อมใช้งาน Dagger ที่สามารถ Inject ของต่างๆให้กับ Fragment ของเราได้แล้ว โดยที่เราไม่ต้องสร้าง Component เหล่านั้นขึ้นมาใน Fragment ของเรา โดยขั้นตอนการเรียกใช้ใน Fragment จะเป็นประมาณนี้ครับ
class DetailFragment : Fragment(), FragmentDetailContract.View {
@Inject
lateinit var presenter: FragmentDetailContract.UserActionListener
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AndroidSupportInjection.inject(this)
}
...
}
เพียงเท่านี้ presenter
ก็จะถูก Inject
เข้าไปใน Fragment
ของเรา พร้อมใช้งานทันที
ใครสนใจดูโค้ดทั้งหมดสามารถไป Checkout ออกมาดูได้ตาม link ด้านล่างนี้ได้ครับ
Conclusion
ในบทความนี้เราได้เรียนรู้การใช้ API ใหม่ของ Dagger2 เพื่อช่วย Inject object ต่างๆให้กับ Fragment ของเรา ซึ่งจะเห็นว่าขึ้นตอนการทำ ก็ไม่ต่างจากการทำ Injection ของ Activity มากนัก แต่อย่างไรก็ตามจะเห็นว่าขั้นตอนการทำจะค่อนข้างซับซ้อนอยู่พอสมควร ซึ่งต้องอาศัยความจำ ความเข้าใจ และการฝึกฝน แต่รับรองว่าหากเราใช้งานมันได้อย่างคล่องแคล่วแล้ว ผลรับที่ได้คุ้มค่าอย่างแน่นอนครับ
สุดท้ายนี้ก็เช่นเคยครับ หากผู้อ่านคิดว่าบทความนี้มีประโยชน์ ก็ฝากกด 👏 เป็นกำลังใจให้ด้วยนะครับ