Dependency
ํ๋์ ํด๋์ค๊ฐ ๋ค๋ฅธ ํ๋์ ํด๋์ค์ ์์กดํ๋ ๊ฒ์ ์๋ฏธํจ. A ํด๋์ค๊ฐ ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ธฐ ์ํด B ํด๋์ค๋ฅผ ํ์๋ก ํ ๋ B๋ A์ ์์กด ๋์์ด ๋จ. ์๋ฅผ ๋ค์ด ์๋ฌ๋๋ฅผ ๋ง๋ค๊ธฐ ์ํด ๋๋ ์ฑ์ด ํ์ํจ. ์๋ฌ๋๋ ๋๋ ์ฑ์ ์์กดํ๊ณ ์์. ๋ฐ๋ผ์ ์๋ฌ๋ ← ๋๋ ์ฑ ๊ด๊ณ๊ฐ ๋จ. ์ฌ๊ธฐ์ ๋๋ ์ฑ์ด ์์กด์ ๋์, Dependency๊ฐ ๋จ.
Injection
์์ ์๋ฅผ ๊ณ์ ์ฌ์ฉํด์ ์๊ฐํด๋ณด๋ฉด ์๋ฌ๋ ํด๋์ค, ๋๋ ์ฑ ํด๋์ค๊ฐ ์์ ๋ ์๋ฌ๋ ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ธฐ ์ํด์๋ ๋๋ ์ฑ์ ๊ฐ์ฒด๊ฐ ํ์ํจ. ํ์ง๋ง ์ด ๋๋ ์ฑ ๊ฐ์ฒด๋ฅผ ์๋ฌ๋ ํด๋์ค ๋ด๋ถ์์ ๋ง๋๋ ๊ฒ์ด ์๋๋ผ ์ธ๋ถ์์ ๋ง๋ค์ด์ ๊ฑด๋ค์ฃผ๋ ๋ฐฉ๋ฒ๋ ์์. ์ด๋ ๊ฒ ์ธ๋ถ์์ ์์กด๊ด๊ณ๊ฐ ์๋ ๋์์ ๊ฐ์ ธ์ค๋ ๊ฒ์ ์์กด์ฑ(Dependency)์ ์ธ๋ถ์์ ์ฃผ์ (Inject)ํด์ค๋ค๊ณ ํจ. ๋ฐ๋ผ์ ๋๋ ์ฑ์ ์ข ๋ฅ๊ฐ ๋ฐ์ฌ๋ฏน ๋๋ ์ฑ์ด์๋๋ฐ ์๊ฑฐํธ ๋๋ ์ฑ์ผ๋ก ๋ฐ๋๋ค๊ณ ์๋ฌ๋ ๋ด๋ถ์ ์ฝ๋๋ฅผ ์์ ํ ํ์ ์์ด ์ฃผ์ ๋ง ๋ฐ์ผ๋ฉด ๋จ. → Test์ ํธํด์ง๊ธฐ๋ ํจ.
DI : Dependency Injection
์ค์ํ ์์ง๋ ์์กด์ฑ์ด ์๋ ํด๋์ค ๋ด๋ถ์์ ์์กด์ ๋์์ด ๋๋ ๊ฐ์ฒด๋ฅผ ์ง์ ์์ฑํ๋ ๊ฒ์ด ์๋๋ผ ์ธ๋ถ์์ ๋๊ฒจ๋ฐ์์ผ ํจ. ๊ทธ๋ ๋ค๋ฉด ๊ทธ ๋ฐฉ๋ฒ์ผ๋ก๋ constructor์์ ๋๊ฒจ์ฃผ๊ฑฐ๋, setter ๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ ๋๊ฒจ์ฃผ๋ ๋ฐฉ์์ด ์์. ์๋๋ก์ด๋์์ ์ฌ์ฉํ๋ ๋ํ์ ์ธ DI ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก๋ Koin, Dagger, Hilt๊ฐ ์์. ์ด์ค Hilt๊ฐ ํ์ต ๊ณก์ ์ด ์๋์ ์ผ๋ก ๋ฎ์ ํธ์ด๋ผ๊ณ ํด์ Hilt์ ๋ํด ๊ณต๋ถํ ์์ ์.
์์กด์ฑ ์ฃผ์ ์ ํ์ ๋ ์ฅ์ ์ผ๋ก๋ ๋ค์๊ณผ ๊ฐ์.
- ์๋ฌ๋๋ฅผ ๋ง๋๋ ์ชฝ๊ณผ ๋๋ ์ฑ์ ๋ง๋๋ ์ชฝ, ์ฆ ์์กด์ฑ์ด ์๋ ํด๋์ค์ ์์กด ๋์์ด ๋ถ๋ฆฌ๋์ด ์์ผ๋ฏ๋ก ์์กด์ฑ์ด ์๋ ํด๋์ค๋ฅผ ํ ์คํธํ ๋ ๋ชจ์๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ฉด ๋ ์ฝ๊ฒ ํ ์คํธ๊ฐ ๊ฐ๋ฅํจ.
- ์์กด ๋์๊ณผ ์์กด์ฑ์ด ์๋ ์ชฝ์ด ๊ฐ๊ฐ ๋ ๋ฆฝ๋์ด ์๊ธฐ ๋๋ฌธ์ ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถ๊ณ ์ ์ง๋ณด์์ ์ฉ์ดํจ.
- ์ฝ๋์ ์ฌ์ฌ์ฉ์ฑ์ด ์ฆ๊ฐํจ.
Hilt
๊ตฌ๊ธ์ Dagger๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ DI ๋ผ์ด๋ธ๋ฌ๋ฆฌ.
Hilt๋ Android ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์กฐ๋ฅผ ๋จ์ํํ๊ณ , ์ ์ง ๊ด๋ฆฌ ๋ฐ ํ์ฅ์ฑ์ ํฅ์์ํค๋ฉฐ, ์ข ์์ฑ ์ฃผ์ ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐํ์ ์ค๋ฒํค๋๋ฅผ ์ค์ผ ์ ์๋๋ก ๋์์ค๋๋ค.
์์ํ๊ธฐ
build.gradle์ ์ข ์์ฑ ์ถ๊ฐ
@HiltAndroidApp
Hilt ์ฌ์ฉํ๋ ๋ชจ๋ ์ฑ์ @HiltAndroidApp Annotation์ผ๋ก ์ง์ ๋ Application Class๋ฅผ ํฌํจํด์ผ๋ง ํจ. @HiltAndroidApp์ Hilt์ ์ฝ๋ ์์ฑ์ ํธ๋ฆฌ๊ฑฐํจ. annotation์ผ๋ก ์์ฑ๋ Hilt ๊ตฌ์ฑ์์๋ Application ๊ฐ์ฒด์ ์๋ช ์ฃผ๊ธฐ์ ์ฐ๊ฒฐ๋จ. ApplicationContext์ Hilt๊ฐ ์ ๊ทผํ ์ผ์ด ๋ง๊ธฐ ๋๋ฌธ์ ์ด ๊ณผ์ ์ด ํ์ํจ. Application Class๋ฅผ ์ฌ์ฉํ์ง ์๋ค๊ฐ ์ถ๊ฐํ๋ค๋ฉด AndroidManifest.xml์ application ํ๊ทธ์ ์ถ๊ฐํด์ฃผ์ด์ผ ํจ.
@HiltAndroidApp
class MainApplication : Application() {
}
Hilt๊ฐ ์ง์ํ๋ Android Class
- Application (@HiltAndroidApp ์ฌ์ฉ)
- ViewModel (@HiltViewModel ์ฌ์ฉ)
- Activity (@AndroidEntrypoint ์ฌ์ฉ)
- Fragment (@AndroidEntrypoint ์ฌ์ฉ)
- View (@AndroidEntrypoint ์ฌ์ฉ)
- Service (@AndroidEntrypoint ์ฌ์ฉ)
- BroadcastReceiver (@AndroidEntrypoint ์ฌ์ฉ)
Hilt๊ฐ ์์ฑํ๋ Component์ ๊ณ์ธต ๊ตฌ์กฐ
์ต์์๋ ApplicationClass๋ฅผ ์ฌ์ฉํ๋ SingletonComponent๊ฐ ๋จ. ๊ฐ Component๋ค์ ๊ทธ ๋ถ๋ชจ๋ก๋ถํฐ Dependency๋ฅผ ๋ฐ์ ์ ์์.
@AndroidEntryPoint
์๋๋ก์ด๋ ํด๋์ค์ @AndroidEntryPoint Annotation์ ์ฌ์ฉํ๋ฉด ์ด ํด๋์ค์ ์ข ์๋ ๋ค๋ฅธ ํด๋์ค์๋ ํด๋น Annotation์ ์ฌ์ฉํด์ผ ํจ. @AndroidEntryPoint๋ ํ๋ก์ ํธ์ ๊ฐ Android ํด๋์ค์ ๋ํ ๊ฐ๋ณ Hilt ๊ตฌ์ฑ์์๋ฅผ ์์ฑํจ
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
@Inject
@Inject ์ฃผ์์ ์ฌ์ฉํ์ฌ ํ๋ ์ฝ์ ์ ํจ. Hilt๊ฐ ์ฝ์ ํ ํ๋๋ private์ด ๋ถ๊ฐ๋ฅํจ. @Inject๋ฅผ ์ฌ์ฉํด์ Constructor Injection๋ ๊ฐ๋ฅํจ.
@AndroidEntryPoint
class ExampleActivity : AppCompatActivity() {
@Inject lateinit var analytics: AnalyticsAdapter
...
}
Hilt @Inject ๋ ์์ธํ๊ฒ - 1
ํ๋์ ์ธ๋ถ ๊ฐ์ฒด๋ฅผ ์ฃผ์ ํด์ฃผ๋ ค๋ฉด Hilt๊ฐ ์ด๋ป๊ฒ ์ธ์คํด์ค๋ฅผ ์ ๊ณตํ๋์ง ์์์ผ ํจ. binding์๋ ํน์ ์ ํ์ ์ธ์คํด์ค๋ฅผ dependency๋ก ์ ๊ณตํ๋๋ฐ ํ์ํ info๊ฐ ๋ค์ด์์. ํด๋์ค์ constructor์ @Inject ์ฃผ์์ ์ฌ์ฉํด์ Hilt์ ์๋ ค์ฃผ์ด์ผ ํจ.
class AnalyticsAdapter @Inject constructor(
private val service: AnalyticsService
) { ... }
@Inject ์ฃผ์์ด ์ง์ ๋ ํด๋์ค์ ์์ฑ์ ๋งค๊ฐ๋ณ์๋ ๊ทธ ํด๋์ค์ ์ข ์ ํญ๋ชฉ์. ์์ ์ฝ๋์์๋ AnalyticsAdapter ํด๋์ค์ ์์ฑ์์ @Inject annotation์ด ์๊ณ ์ด ์์ฑ์๋ AnalyticsService๊ฐ ์ข ์ ํญ๋ชฉ์ผ๋ก ์์. ๊ทธ๋์ Hilt์๊ฒ AnalyticsAdapter ๊ฐ์ฒด๋ฅผ ์์ฑํ ๋ AnalyticsService ๊ฐ์ฒด๋ ์์ฑํ์ฌ ์ฃผ์ ํด์ฃผ์ด์ผ ํจ์ ์๋ ค์ค. ์์ ๊ฐ์ด Constructor Injection์ ์ฌ์ฉํ ๊ฒฝ์ฐ์๋ ๋ค์๊ณผ ๊ฐ์ ์ฅ์ ์ด ์์. Constructor Injection์ ์์ฑ ์ ์ด๋ค ํด๋์ค์ ์ปดํฌ๋ํธ๊ฐ ํ์ํ์ง ๋ช ํํ๊ฒ ์ ์ ์์. → ๊ฐ๋ ์ฑ์ ์ข์.
๋ํ ์์ AnalyticsAdapter ํด๋์ค๋ Component๋ค์์ ์ฌ์ฉ์ด ๊ฐ๋ฅํจ. ์์๋ก๋ @AndroidEntryPoint annotation์ ๋ถ์ธ Activity์์ ์ฌ์ฉ์ด ๊ฐ๋ฅํจ. ์๋ ์ฝ๋๋ Constructor Injection์ด ์๋ Field Injection์ ์ฌ์ฉํ ๋ฐฉ๋ฒ์. ๋ค์ ํ ๋ฒ Field Injection์ ์ ๊ทผ ์ ํ์๋ฅผ private์ผ๋ก ์ ์ธํ๋ฉด ์ ๋จ!
@AndroidEntryPoint
class ActivitySample : AppCompatActivity() {
@Inject
lateinit var analyticsAdapter: AnalyticsAdapter
...
}
โ ๋ด๊ฐ ํท๊ฐ๋ฆฐ ๋ถ๋ถ : @Inject Annotation์ ๊ทธ๋ผ ์ฃผ์ ์ ๋ฐ์ ๋์์ ๋ถ์ด๋๊ฒ ๋ง๋? → No ๐ โ๏ธ
@Inject ์ด๋ ธํ ์ด์ ์ ์ฃผ์ ์ ๋ฐ์ ๋์๊ณผ ์ฃผ์ ์ ์ ๊ณตํ๋ ๋์ ๋ชจ๋์๊ฒ ๋ถ์ฌ์ค์ผ ํฉ๋๋ค.
๋ณดํต ํด๋์ค์ ์์ฑ์์์ ํ์ํ ์์กด์ฑ์ ์ฃผ์ ๋ฐ์ ๋ @Inject ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํฉ๋๋ค. ์ด๋ ์์ฑ์ ๋งค๊ฐ๋ณ์์ @Inject ์ด๋ ธํ ์ด์ ์ด ๋ถ์ ๊ฒฝ์ฐ ํด๋น ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ ๋ค๋ฅธ ํด๋์ค์์ ์ธ์คํด์ค๋ฅผ ์์ฑํ ๋, ์์กด์ฑ์ ์ฃผ์ ํ ์ ์๋๋ก Dagger๋ Hilt์ ๊ฐ์ ํ๋ ์์ํฌ๊ฐ ์๋์ผ๋ก ์์กด์ฑ์ ์ฃผ์ ํฉ๋๋ค.
๋ฐ๋ผ์, ์ฃผ์ ์ ๋ฐ์ ๋์์ ์์ฑ์ ๋งค๊ฐ๋ณ์์ @Inject ์ด๋ ธํ ์ด์ ์ ๋ถ์ฌ์ค์ผ ํ๊ณ , ์์กด์ฑ์ ์ ๊ณตํ๋ ๋์(์๋ฅผ ๋ค์ด, ๋ค๋ฅธ ํด๋์ค๋ ๋ชจ๋)์ ๋ฉ์๋ ๋๋ ํ๋์๋ @Inject ์ด๋ ธํ ์ด์ ์ ๋ถ์ฌ์ค์ผ ํฉ๋๋ค. ์ด๋ฅผ ํตํด ํ๋ ์์ํฌ๊ฐ ์์กด์ฑ์ ์ฃผ์ ํ ๋์์ ์์๋ด๊ณ , ์๋์ผ๋ก ์์กด์ฑ์ ์ฃผ์ ํ ์ ์๋๋ก ํฉ๋๋ค.
๋ํ @Inject๋ Dependency Graph๋ฅผ ์ด์ด์ค๋ค๊ณ ํจ. Hilt๊ฐ inject annotation์ด ๋ถ์ด์๋ :: Dependency๋ฅผ ์ฃผ์ ๋ฐ์ ๊ฐ์ฒด์ Dependency๋ฅผ ์ ๊ณตํด์ ์์ฑํ ๊ฐ์ฒด์ ํด๋์ค์ Dependency Graph๋ฅผ ์ด์ด๋ถ์ฌ์ค.
Hilt ๋ชจ๋
constructor๋ก ์ฃผ์ ์ ์ ๊ณตํ ์ ์๋ ๊ฒฝ์ฐ Hilt ๋ชจ๋์ ์ฌ์ฉํด์ Hilt์ binding info๋ฅผ ์ ๊ณตํจ. constructor๋ก ์ฃผ์ ์ ์ ๊ณตํ ์ ์๋ ๊ฒฝ์ฐ๋ ๋ค์๊ณผ ๊ฐ์.
- ์ธํฐํ์ด์ค
- A interface๋ฅผ B Class, C Class์์ implementํด์ ๊ตฌํํ๋ค๊ณ ๊ฐ์ ํ์ ๋ ๊ฐ ํด๋์ค์ ์์ฑ์์ A interface๋ฅผ ๋ฃ์ด์ฃผ๋ฉด ์๋จ. ์๋ฌ ๋ฐ์.
- ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํด๋์ค
๋ฐ๋ผ์ ์์ ๊ฐ์ ๊ฒฝ์ฐ Hilt ๋ชจ๋์ ์ฌ์ฉํด์ผ ํจ. Hilt ๋ชจ๋์ @Module๋ก Annotation์ด ์ง์ ๋ ํด๋์ค์. ๋ํ @InstallIn ์ฃผ์์ ์ง์ ํ์ฌ ๊ฐ ๋ชจ๋์ ์ฌ์ฉํ ์๋๋ก์ด๋ ํด๋์ค๋ฅผ Hilt์ ์๋ ค์ฃผ์ด์ผ ํจ. Hilt ๋ชจ๋์ ์ ๊ณตํ dependency๋ค์ Hilt ๋ชจ๋์ ์ค์นํ Android Class์ ๋ชจ๋ ๊ตฌ์ฑ์์์์ ์ฌ์ฉ ๊ฐ๋ฅ. ์ฆ @Module annotation์ Hilt์๊ฒ ์ด ํด๋์ค๊ฐ Module์ด ์๋ ๊ณณ์ด๋ผ๊ณ ์๋ ค์ฃผ๋ ๊ฒ์ด๊ณ @InstallIn annotation์ ํด๋น ๋ชจ๋์ด activity์ ๊ฐ์ Hilt ๋ชจ๋์ ์ค์นํ ํด๋์ค์์ ์ฌ์ฉ ๊ฐ๋ฅํ๋ค๊ณ ์ ์ธํ๋ ์๋ฏธ์.
Module์ ์ฌ์ฉํ๋ ๋ฐฉ์์ผ๋ก๋ Provides Annotation๊ณผ Binds Annotation์ด ์์. Provides ๋ฐฉ์์ด ๋ ํธํ๋ค๊ณ ํ๊ณ ์์์ module class๋ฅผ ์์ฑํ๊ณ ์ฌ๊ธฐ์ ํด๋์ค๋ค ์์ฑํ๋ ์์ผ๋ก ์ฌ์ฉํ๋ค๊ณ ํจ.
@Provides
์ผ๋จ Porvides Annotation ๋ฐฉ์์ผ๋ก Module์ ์์ฑํ๋ค๊ณ ํด๋ @Module, @InstallIn annotation์ ๋ถ์ฌ์ฃผ์ด์ผ ํจ. ๊ทธ๋ฆฌ๊ณ ๋ชจ๋ ํด๋์ค ๋ด๋ถ์ @Provides annotation์ ์ฌ์ฉํด์ Provides ํจ์๋ค์ ๋ฃ์ด์ฃผ์ด์ผ ํจ.
https://developer88.tistory.com/349 ์ด ๋ธ๋ก๊ทธ๋ฅผ ๋ณด๊ณ ๋ฐ๋ผํ ์์๋ ๋ค์๊ณผ ๊ฐ์.
์ฒซ ๋ฒ์งธ ์ฝ๋์ ๋ํ ์ค๋ช : ClassBForInterfaceTest ํด๋์ค๋ InterfaceA๋ฅผ Implementsํ ํด๋์ค์ด๊ณ ๋ฐ๋ผ์ showString() method๋ฅผ overrideํจ. ์ด ๋ฉ์๋์์๋ stringDependency๋ผ๋ ๋ณ์๋ฅผ ํฌํจํ๋ ์คํธ๋ง์ ๋ฆฌํดํ๋๋ฐ ์ด stringDependency ๋ณ์๋ ClassBForInterfaceTest ํด๋์ค๊ฐ ์์ฑ์ ์ฃผ์ ๋๋๋ก ์์ฑ์์์ @Inject ํค์๋๋ฅผ ์ฌ์ฉํด ์ ์ธํด๋์์. ๊ทธ๋ฆฌ๊ณ ClassAForInterfaceTest ํด๋์ค๋ ์์ฑ ์ InterfaceA๋ฅผ ์ฃผ์ ๋ฐ๋๋ก ํด๋์ค ์์ฑ์์ @Inject ํค์๋๋ฅผ ์ฌ์ฉํด ์ ์ธํด๋์.
๋ ๋ฒ์งธ ์ฝ๋์ ๋ํ ์ค๋ช : ClassBForInterfaceTest๋ ์ธํฐํ์ด์ค๋ฅผ ์ฃผ์ ๋ฐ์์ผ ํจ. ๋ฐ๋ผ์ Module์ ์์ฑํด์ฃผ์ด์ผ ํ๋ฏ๋ก @Module, @InstallIn annotation์ ์ฌ์ฉํจ. @InstallIn annotation์ ์ฌ์ฉํด ์ด ๋ชจ๋์ด ์ฌ์ฉ๋๋ ์ปดํฌ๋ํธ๋ฅผ ActivityComponent๋ก ์ง์ ํด์คฌ์. → ์ด ๋ง์ ์ฆ Activity์์ ์ด ์์กด์ฑ์ ์ฌ์ฉํ๊ฒ ๋ค๊ณ ๋งํ๋ ๊ฒ์. ๋ฐ๋ผ์ Activity class์์ @Inject๋ฅผ ์ฌ์ฉํด์ ์์กด์ฑ์ ์ฃผ์ ํด์ค ์ ์๊ฒ ๋จ.
interface InterfaceA {
fun showString(): String
}
class ClassAForInterfaceTest @Inject constructor(private val bClass: InterfaceA) {
fun doTestA(): String {
return bClass.showString()
}
}
class ClassBForInterfaceTest
@Inject constructor(private val stringDependency: String) : InterfaceA {
override fun showString(): String {
return "stringDependency : ${stringDependency}"
}
}
@Module
@InstallIn(ActivityComponent::class)
class ModuleForInterface {
@Provides
fun provideString(): String {
return "dependency String by ModuleForInterface"
}
@Provides
fun testProvides(cString: String): InterfaceA {
return ClassBForInterfaceTest(cString)
}
}
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var classAForInterfaceTest : ClassAForInterfaceTest
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val text = classAForInterfaceTest.doTestA()
println(text)
}
}
โ๊ถ๊ธํด์ ํด๋ณธ ๊ฒ : @Module์์ string ํ์ ์ ์ฃผ์ ํด์ฃผ๊ณ ์๋๋ฐ String์ ์ฃผ์ ํด์ฃผ๋ ํจ์๊ฐ ์ฌ๋ฌ๊ฐ๋ผ๋ฉด..? → ๋ญ provide ํด์ค์ง ๋ชจ๋ฅด๋ฏ๋ก ์ค๋ฅ ๋ฐ์
@Module
@InstallIn(ActivityComponent::class)
class ModuleForInterface {
@Provides
fun provideString(): String {
return "dependency String by ModuleForInterface"
}
@Provides
fun provideString2(): String {
return "dependency String 2 by ModuleForInterface"
}
@Provides
fun testProvides(cString: String): InterfaceA {
return ClassBForInterfaceTest(cString)
}
}
โ๊ถ๊ธํด์ ํด๋ณธ ๊ฒ 2 : @Module์์ string ํ์ ์ ์ฃผ์ ํด์ฃผ๊ณ ์๋๋ฐ String์ ์ฃผ์ ํด์ฃผ๋ ํจ์๊ฐ ์ฌ๋ฌ๊ฐ์ธ๋งํผ ๋ฐ๋ ๊ฒ๋ ์๋ค๋ฉด..? → ๋์ผํ๊ฒ ๋ญ provide ํด์ค์ง ๋ชจ๋ฅด๋ฏ๋ก ์ค๋ฅ ๋ฐ์. ํ์ง๋ง ํ์ ์ด ๋ค๋ฅธ ๊ฒ์ ok์ฌ์! (์๋ ์ฝ๋์์ Int๋ Ok)
@Module
@InstallIn(ActivityComponent::class)
class ModuleForInterface {
@Provides
fun provideString(): String {
return "dependency String by ModuleForInterface"
}
@Provides
fun provideString2(): String {
return "dependency String 2 by ModuleForInterface"
}
@Provides
fun provideInt(): Int {
return 1
}
@Provides
fun testProvides(cString: String, cString2: String, num: Int): InterfaceA {
Log.d("num", "$num")
Log.d("string", "$cString2")
return ClassBForInterfaceTest(cString)
}
}
ํ์ง๋ง ์์ ๊ถ๊ธํด์ ํด๋ดค๋ค๊ณ ํ ๊ฑด ์ญ์ ๋ฐฉ๋ฒ์ด ์์์. ๋ฐ๋ก @Qualifier, @Retention annotation์ ์ฌ์ฉํ๋ฉด ๋จ. ์ด ๋ฐฉ๋ฒ์ ํ์ ์ ์ฃผ์ ์ด๋ผ๊ณ ํ๋ค๊ณ ํจ.
@Qualifier, @Retention
AI๊ฐ ์ค๋ช ํด์ค ์ฝ๋
์๋ ์ฝ๋๋ ๋์ํฉ๋๋ค. ๊ทธ ์ด์ ๋ QualifierTest ๋ชจ๋์ด SingletonComponent์ @InstallIn ๋์๊ธฐ ๋๋ฌธ์ QualifierInterfaceType1๊ณผ QualifierInterfaceType2 ์ด๋ ธํ ์ด์ ์ ๊ฐ์ง Provides ๋ฉ์๋๊ฐ Singleton ๋ฒ์ ๋ด์์ ์ ์ผํ๊ฒ ์กด์ฌํฉ๋๋ค. ์ด๋ ๊ฒ ํ์ ์๋ฅผ ์ฌ์ฉํ์ฌ ์์กด์ฑ์ ๊ตฌ๋ถํ๋ ๋ฐฉ์์ "ํ์ ์ ์ฃผ์ "์ด๋ผ๊ณ ํฉ๋๋ค.
๋ฐ๋ผ์, QualifierTestClassA ์์ฑ์์์ QualifierInterfaceType1๊ณผ QualifierInterfaceType2 ํ์ ์๋ฅผ ์ด์ฉํด ๊ฐ๊ฐ์ ๊ตฌํ์ฒด๋ฅผ ์ฃผ์ ๋ฐ์ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด, QualifierTestClassA์์ concatString() ๋ฉ์๋๋ฅผ ํธ์ถํ๋ฉด QualifierTestClassB์ QualifierTestClassC๊ฐ ๊ตฌํํ showString() ๋ฉ์๋์ ๊ฒฐ๊ณผ๋ฅผ ํฉ์น ๋ฌธ์์ด์ด ๋ฐํ๋ฉ๋๋ค.
// Module
@Module
@InstallIn(SingletonComponent::class)
class QualifierTest {
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class QualifierInterfaceType1
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class QualifierInterfaceType2
@QualifierInterfaceType1
@Provides
fun testProvides1(): QualifierInterface {
return QualifierTestClassB()
}
@QualifierInterfaceType2
@Provides
fun testProvides2(): QualifierInterface {
return QualifierTestClassC()
}
}
// Class
class QualifierTestClassA @Inject constructor(
@QualifierTest.QualifierInterfaceType1 private val qInterfaceType1: QualifierInterface,
@QualifierTest.QualifierInterfaceType2 private val qInterfaceType2: QualifierInterface
){
fun concatString(): String{
return qInterfaceType1.showString() + qInterfaceType2.showString()
}
}
class QualifierTestClassB @Inject constructor(): QualifierInterface {
override fun showString(): String {
return "QualifierTestClassB"
}
}
class QualifierTestClassC @Inject constructor(): QualifierInterface {
override fun showString(): String {
return "QualifierTestClassC"
}
}
interface QualifierInterface {
fun showString(): String
}
// Activity
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var qualifierTestClassA: QualifierTestClassA
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// qualifier test
val text = qualifierTestClassA.concatString()
println(text)
}
}
@Binds
@Binds ์ฃผ์์ ์ธํฐํ์ด์ค์ ์ธ์คํด์ค๋ฅผ ์ ๊ณตํด์ผํ ๋ ์ฌ์ฉํ ๊ตฌํ์ Hilt์ ์๋ ค์ค. ๊ทธ๋ฌ๋ @Binds ๋ฐฉ์์ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์๋ ์ฌ์ฉํ ์ ์์.
class BindTestClassA @Inject constructor(
private val bindTestClassB: BindInterface
){
fun testBindTestClassA(): String {
return bindTestClassB.showString()
}
}
class BindTestClassB @Inject constructor(): BindInterface{
override fun showString(): String {
return "BindTestClassB"
}
}
interface BindInterface {
fun showString(): String
}
@Module
@InstallIn(ActivityComponent::class)
abstract class ModuleForBind {
@ActivityScoped
@Binds
abstract fun bindInterfaceDependency(bindTestClassB: BindTestClassB): BindInterface
}
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject
lateinit var bindTestClassA: BindTestClassA
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val text = bindTestClassA.testBindTestClassA()
println(text)
}
}
AI๊ฐ ์ค๋ช ํด์ค @Module ๋ถ๋ถ ์ฝ๋
@Module ์ด๋ ธํ ์ด์ ์ด ๋ถ์ ํด๋์ค๋ Hilt์์ ์์กด์ฑ ์ฃผ์ ์ ์ํํ ๋ ํ์ํ ๋ฐ์ธ๋ฉ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ ์ญํ ์ ํฉ๋๋ค. ๋ฐ์ธ๋ฉ ์ ๋ณด๋ ์ด๋ค ํด๋์ค๋ฅผ ์ด๋ค ์ธํฐํ์ด์ค๋ ์ถ์ ํด๋์ค์ ๋ฐ์ธ๋ฉํ ๊ฒ์ธ์ง๋ฅผ ์ ์ํ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
@Module ์ด๋ ธํ ์ด์ ์๋ @InstallIn ์ด๋ ธํ ์ด์ ์ ํจ๊ป ์ฌ์ฉํ์ฌ ์ด ๋ชจ๋์ด ์ด๋ค ์ปดํฌ๋ํธ์ ์ค์น๋์ด์ผ ํ๋์ง๋ฅผ ์ง์ ํฉ๋๋ค. ์ด๋ฒ ์์์์๋ ActivityComponent์ ์ค์น๋ฉ๋๋ค.
@Module ์ด๋ ธํ ์ด์ ์ด ๋ถ์ ํด๋์ค ๋ด๋ถ์๋ @Provides, @Binds, @Multibinds ๋ฑ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ธ๋ฉ ์ ๋ณด๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ด ์ค์์ @Binds ์ด๋ ธํ ์ด์ ์ ์ถ์ ํด๋์ค๋ ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ ํด๋์ค๋ฅผ ๋ฐ์ธ๋ฉํ ๋ ์ฌ์ฉํฉ๋๋ค.
@Module ์ด๋ ธํ ์ด์ ์ด ๋ถ์ ํด๋์ค์ธ ModuleForBind ๋ด๋ถ์๋ ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๊ฐ ์์ต๋๋ค.@Binds abstract fun bindInterfaceDependency(bindTestClassB: BindTestClassB): BindInterface
์ด ์ฝ๋๋ BindTestClassB ํด๋์ค๊ฐ ๊ตฌํํ๋ BindInterface ์ธํฐํ์ด์ค๋ฅผ ๋ฐ์ธ๋ฉํ๋ ์ฝ๋์ ๋๋ค. ์ฆ, BindTestClassB ๊ฐ์ฒด๊ฐ BindInterface ํ์ ์ผ๋ก ์ฃผ์ ๋ ์ ์๋๋ก Hilt์๊ฒ ๋ฐ์ธ๋ฉ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ ๊ฒ์ ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์ด์ MainActivity์์ BindTestClassA๋ฅผ ์ฃผ์ ๋ฐ์ ์ฌ์ฉํ ๋, Hilt๋ BindTestClassB ๊ฐ์ฒด๋ฅผ BindInterface ํ์ ์ผ๋ก ์ฃผ์ ํด์ค๋๋ค. ์ด๋ฅผ ํตํด BindTestClassA์์๋ BindInterface์ ์ ์๋ showString() ๋ฉ์๋๋ฅผ ํธ์ถํ ์ ์์ต๋๋ค.
ViewModel
@HiltViewModel์ ์ฌ์ฉํ๋ฉด ๋จ. ์ดํ @AndroidEntryPoint ์ฃผ์์ผ๋ก ์ง์ ๋ ์กํฐ๋นํฐ๋ ํ๋๊ทธ๋จผํธ์์ ****by viewModels() ํค์๋๋ก ๋ทฐ๋ชจ๋ธ์ ๊ฐ์ ธ์ฌ ์ ์์.
@HiltViewModel
class MainViewModel @Inject constructor(
@ModuleForViewModel.ViewModelIntType1 private val a: Int,
@ModuleForViewModel.ViewModelIntType2 private val b: Int
): ViewModel() {
fun getNum(): Int {
return a + b
}
}
@Module
@InstallIn(ViewModelComponent::class)
class ModuleForViewModel {
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class ViewModelIntType1
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class ViewModelIntType2
@ViewModelIntType1
@Provides
fun provideA(): Int = 1
@ViewModelIntType2
@Provides
fun provideB(): Int = 3
}
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val mainViewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val text = mainViewModel.getNum().toShort()
println(text)
}
}
AI๊ฐ ์ค๋ช ํด์ค ์ฝ๋
์ด ์ฝ๋๋ ์๋๋ก์ด๋์ Hilt ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ์์ฑ๋ ๋ทฐ ๋ชจ๋ธ์ ๊ฐ์ ์ฌ์ฉํ๋ ์์ ์ ๋๋ค.
์ฐ์ @HiltViewModel ์ด๋ ธํ ์ด์ ์ ViewModel์ ๋ํ ์์กด์ฑ ์ฃผ์ ์ ์ฌ์ฉํ๊ธฐ์ํ Hilt-specific ์ด๋ ธํ ์ด์ ์ ๋๋ค.
MainViewModel ํด๋์ค๋ @HiltViewModel ์ด๋ ธํ ์ด์ ์ ์ฌ์ฉํ์ฌ ๋ทฐ ๋ชจ๋ธ์์ ํ์ํฉ๋๋ค. ์ด ํด๋์ค๋ ๋ ๊ฐ์ ์ ์ a์ b๋ฅผ ์ธ์๋ก ๋ฐ์ต๋๋ค. a์ b๋ ModuleForViewModel ๋ชจ๋์ ๋ ๊ฐ์ ์ด๋ ธํ ์ด์ ์ ์ํด ์ ๊ณต๋๋ @Provides ๋ฉ์๋๋ก ์ฃผ์ ๋ฉ๋๋ค.
@ModuleForViewModel ๋ชจ๋์๋ ๋ทฐ ๋ชจ๋ธ์์ ํ์ํ a์ b ๊ฐ์ ๋ํ ์ ๊ณต์๊ฐ ํฌํจ๋์ด ์์ต๋๋ค. ์ด ๋ชจ๋์ ViewModelComponent ์ค์ฝํ์ ์ค์น๋์ด ์์ผ๋ฉฐ ViewModel์ ๋ํ ์์กด์ฑ์ ์ฃผ์ ํ๊ธฐ ์ํด ์ฌ์ฉ๋ฉ๋๋ค.
๋ง์ง๋ง์ผ๋ก MainActivity ํด๋์ค๋ Hilt๋ฅผ ์ฌ์ฉํ์ฌ MainViewModel ๊ฐ์ฒด๋ฅผ ์์ฑํฉ๋๋ค. ์์ฑ๋ MainViewModel ๊ฐ์ฒด์ getNum() ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ a์ b๋ฅผ ๋ํ๊ณ ๊ฒฐ๊ณผ๋ฅผ ์ถ๋ ฅํฉ๋๋ค.
๋ฏธํด๊ฒฐ ์๋ฌธ
โhilt๋ฅผ ์ฌ์ฉํด์ viewmodel์ applicationcontext๋ฅผ ์์ ํ๊ฒ ์ ๊ณตํ๋ ๋ฐฉ๋ฒ
๋ ํผ๋ฐ์ค
- https://developer.android.com/training/dependency-injection/hilt-android?hl=ko
- https://developer88.tistory.com/172
- https://developer88.tistory.com/349 → ๊ฐ์ธ์ ์ผ๋ก ๊ณต์ ๋ฌธ์์ ์ด ๋ถ ๋ธ๋ก๊ทธ ๊ฒ์๊ธ์ ๋ณด๋๊ฑธ ๊ฐ์ฅ ์ถ์ฒํจ.
- https://velog.io/@haanbink/Android-Hilt-์ฌ์ฉํ๊ธฐ
- https://developer.android.com/training/dependency-injection/hilt-jetpack?hl=ko#viewmodelscoped → Hilt, ViewModel
'CS > Android, Kotlin' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Android] BottomNavigationView Custom (1) | 2023.12.03 |
---|---|
[Android] Base Activity, Fragment (0) | 2023.04.16 |
[Android] OKHttp3๋ฅผ ์ฌ์ฉํ ํค๋ ์ถ๊ฐ, Retrofit2๋ฅผ ์ฌ์ฉํ http ํต์ (0) | 2023.04.16 |
[Android] Custom View (0) | 2023.04.15 |
[Android] Data Binding (0) | 2023.04.15 |