Slide 1

Slide 1 text

NAVER WEBTOON | 안성용 의 존 성 주 이 ᆸ 과 Dagger Hilt

Slide 2

Slide 2 text

Sungyong An NAVER WEBTOON Android GDE @fornewid Link: h tt ps://speakerdeck.com/fornewid

Slide 3

Slide 3 text

(Dependency Injection) ? ? 🤔

Slide 4

Slide 4 text

Dagger Hilt ? ? 🤔

Slide 5

Slide 5 text

? ? 🤔

Slide 6

Slide 6 text

Dagger Hilt . 목표

Slide 7

Slide 7 text

(Dependency Injection) 1

Slide 8

Slide 8 text

(Dependency) ? (Reference) . . , Car Engine . Car Engine Dependency

Slide 9

Slide 9 text

(Dependency Injection) (DI) . . Car Engine

Slide 10

Slide 10 text

class Car { private val engine: Engine = Engine() // construct + use fun start() { engine.start() } } fun main(args: Array) { val car = Car() car.start() } Car Engine Current

Slide 11

Slide 11 text

class Car( private val engine: Engine // use ) { fun start() { engine.start() } } fun main(args: Array) { val engine = Engine() // construct val car = Car(engine) car.start() } Car With DI (Constructor Injection) Engine

Slide 12

Slide 12 text

class Car { lateinit var engine: Engine // use fun start() { engine.start() } } fun main(args: Array) { val car = Car() car.engine = Engine() // construct car.start() } With DI (Field Injection) Car Engine

Slide 13

Slide 13 text

Engine , Car . fun main(args: Array) { val electricCar = Car(ElectricEngine()) val combustionCar = Car(CombustionEngine()) ... } Reuse Car

Slide 14

Slide 14 text

? 🙋

Slide 15

Slide 15 text

class CarTest { @Test fun `Car happy path`() { val car = Car(FakeEngine()) ... } @Test fun `Car with failing Engine`() { val car = Car(FakeFailingEngine()) ... } } Ease of Testing

Slide 16

Slide 16 text

DI . Car Engine

Slide 17

Slide 17 text

: (Service Locator) (DI) . (SL) , . Service Locator Car Engine

Slide 18

Slide 18 text

class Car { private val engine: Engine = Engine() } fun main(args: Array) { val car = Car() car.start() } Car Engine Current

Slide 19

Slide 19 text

class Car( private val engine: Engine ) { ... } fun main(args: Array) { val engine = Engine() val car = Car(engine) car.start() } Car Dependency Injection Engine

Slide 20

Slide 20 text

class Car { private val engine: Engine = ServiceLocator.get() } object ServiceLocator { fun getEngine() : Engine = Engine() } fun main(args: Array) { val car = Car() car.start() } Service Locator Engine Car Service Locator

Slide 21

Slide 21 text

(SL) (DI) : . . , . Singleton . , .

Slide 22

Slide 22 text

? . Android Studio Dagger/Hilt . DI . DI , . Link: h tt ps://medium.com/androiddevelopers/dagger-navigation-suppo rt -in-android-studio-49aa5d149ec9

Slide 23

Slide 23 text

(Manual DI) 2

Slide 24

Slide 24 text

Before . , DB Activity , Singleton Activity . ExampleActivity Network Database

Slide 25

Slide 25 text

ExampleViewModel Before ViewModel Singleton . ExampleActivity Network Database

Slide 26

Slide 26 text

class ExampleViewModel : ViewModel() { fun doSomething() { Database.getSomething().subscribe(...) Network.getSomething().subscribe(...) } } object Database { ... } object Network { ... } Before

Slide 27

Slide 27 text

Database Network . After: Manual DI ExampleActivity Network Database ExampleViewModel

Slide 28

Slide 28 text

class ExampleViewModel( private val database: Database, private val network: Network, ) : ViewModel() { fun doSomething() { database.getSomething().subscribe(...) network.getSomething().subscribe(...) } } class Database { ... } class Network { ... } Manual DI

Slide 29

Slide 29 text

class ExampleActivity: Activity() { private lateinit var viewModel: ExampleViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val database = Database() // Singleton੉ ইפ׮. val network = Network() // ૊, ݒߣ ࢤࢿػ׮. viewModel = ExampleViewModel(database, network) viewModel.doSomething() } } Manual DI

Slide 30

Slide 30 text

class ExampleActivity: Activity() { private lateinit var viewModel: ExampleViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val database = Database() // Singleton੉ ইפ׮. val network = Network() // ૊, ݒߣ ࢤࢿػ׮. viewModel = ExampleViewModel(database, network) viewModel.doSomething() } } Manual DI

Slide 31

Slide 31 text

Manual DI ViewModel Boilerplate , ExampleViewModel . . ExampleViewModel Database Network . . Database Network Singleton . , .

Slide 32

Slide 32 text

class AppContainer { val database = Database() val network = Network() } class MyApplication : Application() { val appContainer = AppContainer() }

Slide 33

Slide 33 text

class ExampleActivity: Activity() { private lateinit var viewModel: ExampleViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val appContainer = (application as MyApplication).appContainer viewModel = ExampleViewModel(appContainer.database, appContainer.network) viewModel.doSomething() } }

Slide 34

Slide 34 text

Android . Example Activity Example ViewModel Network Database

Slide 35

Slide 35 text

Android . Example Activity Example ViewModel Example Repository Example LocalDataSource Example RemoteDataSource Network Database

Slide 36

Slide 36 text

class AppContainer { private val database = Database() private val network = Network() private val localDataSource = ExampleLocalDataSource(database) private val remoteDataSource = ExampleRemoteDataSource(network) val exampleRepository = ExampleRepository(localDataSource, remoteDataSource) } class MyApplication : Application() { val appContainer = AppContainer() }

Slide 37

Slide 37 text

class ExampleActivity: Activity() { private lateinit var viewModel: ExampleViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val appContainer = (application as MyApplication).appContainer viewModel = ExampleViewModel(appContainer.exampleRepository) viewModel.doSomething() } }

Slide 38

Slide 38 text

Singleton . Example Activity Example ViewModel Example Repository Example LocalDataSource Example RemoteDataSource Network Database Custom Scope

Slide 39

Slide 39 text

// জࠁ׮ ੘਷ ߧਤ੄ ੄ઓࢿ ஶప੉ց class ExampleContainer(val exampleRepository: ExampleRepository) { val exampleData = ExampleData() val exampleViewModelFactory = ExampleViewModelFactory(exampleRepository) } class AppContainer { ... val exampleRepository = ExampleRepository(localDataSource, remoteDataSource) var exampleContainer: ExampleContainer? = null } : Custom Scope

Slide 40

Slide 40 text

class ExampleActivity: Activity() { private lateinit var viewModel: ExampleViewModel private lateinit var appContainer: AppContainer private lateinit var data: ExampleData override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) appContainer = (application as MyApplication).appContainer appContainer.exampleContainer = // ੄ઓࢿ ஶప੉ց ࢤࢿ ExampleContainer(appContainer.exampleRepository) viewModel = appContainer.exampleContainer.exampleViewModelFactory.create() data = appContainer.exampleContainer.exampleData } ... : Custom Scope

Slide 41

Slide 41 text

class ExampleActivity: Activity() { private lateinit var viewModel: ExampleViewModel private lateinit var appContainer: AppContainer private lateinit var data: ExampleData ... override fun onDestroy() { appContainer.exampleContainer = null // ੄ઓࢿ ஶప੉ց ࢏ઁ super.onDestroy() } } } : Custom Scope

Slide 42

Slide 42 text

DI . Android ߂ Java ਊ ੄ઓࢿ ઱ੑ ೐ۨ੐ਕ௼. ஹ౵ੌ ఋ੐ী ࣗझ௏٘ ࢤࢿ. Dependency Injection ಁఢ. https://github.com/google/dagger Dagger ӝ߈ Android ੄ઓࢿ ઱ੑ ೐ۨ੐ਕ௼. ؀ࠗ࠙੄ Dagger ࢚ਊҳ ઁѢ. Dependency Injection ಁఢ. https://github.com/google/dagger Dagger Hilt (Recommended) Dagger੄ ੄ઓࢿ ઱ੑਸ ؊ औѱ ݅٘ח Kotlin ஹ౵ੌ۞ ೒۞Ӓੋ. Dependency Injection ಁఢ. https://github.com/square/anvil Anvil Kotlin ҃۝ ੄ઓࢿ ઱ੑ ೐ۨ੐ਕ௼. ۠ఋ੐ী ੄ઓࢿ Ӓې೐ ҳࢿ. Service Locator ಁఢ. https://github.com/InsertKoinIO/koin Kotlin ਊ ੄ઓࢿ ઱ੑ ۄ੉࠳۞ܻ. ஹ౵ੌ ఋ੐ী ੄ઓࢿ Ӓې೐ ਬബࢿ Ѩࢎ. KSP, Kotlin Multiplatform ૑ਗ. https://github.com/evant/kotlin-inject Koin kotlin-inject …

Slide 43

Slide 43 text

Dagger Hilt 3

Slide 44

Slide 44 text

Dagger Hilt Android . Android , . (Application, Activity, Service, BroadcastReceiver, ViewModel) Dagger , . Jetpack Hilt .

Slide 45

Slide 45 text

@Module @InstallIn(SingletonComponent::class) class EngineModule { @Provides // construct fun providesEngine(): Engine = Engine() } Engine @Provides ١ਸ ੉ਊೞৈ, ੄ઓࢿਸ ઁҕೡ ࣻ ੓׮.

Slide 46

Slide 46 text

@Module @InstallIn(SingletonComponent::class) class EngineModule { @Provides // construct fun providesEngine(): Engine = Engine() } // Dependency Injection (Constructor Injection) class Car @Inject constructor( // use private val engine: Engine, ) Car Engine @Injectܳ ੉ਊೞৈ, ੄ઓࢿਸ ௿ېझ ࢤࢿ੗ী ઱ੑೡ ࣻ ੓׮.

Slide 47

Slide 47 text

@Module @InstallIn(SingletonComponent::class) class EngineModule { @Provides fun providesEngine(): Engine = Engine() } // Dependency Injection (Constructor Injection) class Car @Inject constructor( // construct private val engine: Engine, ) @AndroidEntryPoint class ExampleActivity : ComponentActivity() { // Dependency Injection (Field Injection) @Inject // use lateinit var car: Car } Car ExampleActivity Engine Android ௿ېझח ࢤࢿ੗о ইצ, ೙٘ী ੄ઓࢿਸ ઱ੑೠ׮.

Slide 48

Slide 48 text

@Module @InstallIn(SingletonComponent::class) class EngineModule { @Provides fun providesEngine(): Engine = Engine() } // Dependency Injection (Constructor Injection) class Car @Inject constructor( private val engine: Engine, ) @AndroidEntryPoint class ExampleActivity : ComponentActivity() { // Dependency Injection (Field Injection) @Inject lateinit var car: Car } = public @interface Module { ... } = public @interface InstallIn { ... } = public @interface Provides {} = public @interface Inject {} = public @interface AndroidEntryPoint { ... } = public @interface Inject {} Annotationਸ ӝ߈ਵ۽, ੄ઓࢿਸ ઱ੑೞח ௏٘ܳ ࢤࢿ೧ળ׮.

Slide 49

Slide 49 text

Code generation Hilt Dagger . Dagger .

Slide 50

Slide 50 text

// project’s root build.gradle buildscript { repositories { mavenCentral() } dependencies { classpath 'com.google.dagger:hilt-android-gradle-plugin:2.48' } } // module’s build.gradle apply plugin: 'com.google.dagger.hilt.android' Gradle Build Setup

Slide 51

Slide 51 text

// module’s build.gradle dependencies { implementation 'com.google.dagger:hilt-android:2.48' // For Java annotationProcessor 'com.google.dagger:hilt-compiler:2.48' // For Kotlin kapt 'com.google.dagger:hilt-compiler:2.48' // or ksp 'com.google.dagger:hilt-compiler:2.48' } Gradle Build Setup

Slide 52

Slide 52 text

Hilt Basics Dagger Hilt . @HiltAndroidApp @AndroidEntryPoint, @EntryPoint Components SingletonComponent, ViewModelComponent, ActivityComponent, ... Scopes @Singleton, @ViewModelScoped, @ActivityScoped, … Modules @Inject, @Module, @Provides, @Binds, @Quali fi er, …

Slide 53

Slide 53 text

Hilt Application Hilt Application @HiltAndroidApp . , Hilt . @Inject , . @HiltAndroidApp class MyApplication : Application() { @Inject lateinit var data: ExampleData } MyApplication ExampleData

Slide 54

Slide 54 text

Android Android @AndroidEntryPoint . @Inject , . @AndroidEntryPoint class ExampleActivity : ComponentActivity() { @Inject lateinit var data: ExampleData } ... ExampleActivity ExampleData

Slide 55

Slide 55 text

@AndroidEntryPoint class ExampleFragment : Fragment() { ... } @AndroidEntryPoint class ExampleView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, ) : View(context, attrs, defStyleAttr) { ... } @AndroidEntryPoint class ExampleService : Service() { ... } @AndroidEntryPoint class ExampleBroadcastReceiver : BroadcastReceiver() { ... }

Slide 56

Slide 56 text

@AndroidEntryPoint // X class ExampleContentProvider : ContentProvider() { ... } // Activity, Fragment, View, Service, BroadcastReceiverо ইצ, // ׮ܲ Android ௿ېझীח @AndroidEntryPointܳ ࢎਊೡ ࣻ হ׮.

Slide 57

Slide 57 text

Hilt @EntryPoint . Context Hilt . Hilt @EntryPoint @InstallIn(SingletonComponent::class) interface ExampleEntryPoint { fun foo(): Foo } val entryPoint = EntryPointAccessors.fromApplication( appContext, // Context ExampleEntryPoint::class.java, ) val foo = entryPoint.foo()

Slide 58

Slide 58 text

val entryPoint = EntryPointAccessors.fromActivity( activity, // Activity ExampleEntryPoint::class.java, ) val entryPoint = EntryPointAccessors.fromFragment( fragment, // Fragment ExampleEntryPoint::class.java, ) val entryPoint = EntryPointAccessors.fromView( view, // View ExampleEntryPoint::class.java, )

Slide 59

Slide 59 text

Hilt Bindings Android , . @Inject , . @Singleton class ExampleRepository @Inject constructor( private val local: ExampleLocalDataSource, private val remote: ExampleRemoteDataSource, ) { ... } ExampleRepository

Slide 60

Slide 60 text

. Hilt Module Binding . @Module @InstallIn(SingletonComponent::class) class AppModule { @Singleton @Provides fun providesExampleRepository(...): ExampleRepository { return ExampleRepositoryImpl(...) } } Hilt Modules

Slide 61

Slide 61 text

@Module @InstallIn(SingletonComponent::class) class AppModule { @Singleton @Provides fun providesExampleRepository( local: ExampleLocalDataSource, remote: ExampleRemoteDataSource, ): ExampleRepository { return ExampleRepository(local, remote) } } Hilt Modules: @Provides ExampleRepository

Slide 62

Slide 62 text

interface ExampleRepository @Singleton class ExampleRepositoryImpl @Inject constructor(...) : ExampleRepository { ... } @Module @InstallIn(SingletonComponent::class) interface AppModule { @Binds fun bindsExampleRepository( impl: ExampleRepositoryImpl, ): ExampleRepository } Hilt Modules: @Binds ExampleRepositoryImpl ExampleRepository

Slide 63

Slide 63 text

, . ( ) Binding @Qualifier . @Module @InstallIn(SingletonComponent::class) object DispatchersModule { @Provides fun providesDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default = @Provides fun providesIoDispatcher(): CoroutineDispatcher = Dispatchers.IO } Hilt Modules: @Qualifier

Slide 64

Slide 64 text

@Qualifier @Retention(AnnotationRetention.BINARY) annotation class DefaultDispatcher @Qualifier @Retention(AnnotationRetention.BINARY) annotation class IoDispatcher Hilt Modules: @Qualifier

Slide 65

Slide 65 text

@Qualifier @Retention(AnnotationRetention.BINARY) annotation class DefaultDispatcher // key @Qualifier @Retention(AnnotationRetention.BINARY) annotation class IoDispatcher // key Hilt Modules: @Qualifier

Slide 66

Slide 66 text

@Module @InstallIn(SingletonComponent::class) object DispatchersModule { @DefaultDispatcher @Provides fun providesMainDispatcher(): CoroutineDispatcher = Dispatchers.Default @IoDispatcher @Provides fun providesIoDispatcher(): CoroutineDispatcher = Dispatchers.IO } Hilt Modules: @Qualifier

Slide 67

Slide 67 text

class ExampleUseCase @Inject constructor( @DefaultDispatcher defaultDispatcher: CoroutineDispatcher, ) { ... } @Module @InstallIn(SingletonComponent::class) object AppModule { @Provides fun providesExampleRepository( @IoDispatcher ioDispatcher: CoroutineDispatcher, ): ExampleRepository { ... } } Hilt Modules: @Qualifier

Slide 68

Slide 68 text

Hilt Modules: Predefined @Qualifier Hilt @Qualifier . (@ApplicationContext, @ActivityContext) Context . class ExampleRepository @Inject constructor( @ApplicationContext private val appContext: Context, ) { ... } class AnalyticsAdapter @Inject constructor( @ActivityContext private val activityContext: Context, ) { ... }

Slide 69

Slide 69 text

@Module @Provides @Binds @Inject @AndroidEntryPoint @EntryPoint @HiltAndroidApp Overview @Qualifier

Slide 70

Slide 70 text

Component . Scope Annotation . Component / Scope

Slide 71

Slide 71 text

@InstallIn , @Module Component . @Module @InstallIn(SingletonComponent::class) object ExampleModule { @Provides fun providesBar(foo: Foo): Bar = BarImpl(foo) @Singleton @Provides fun providesFoo(): Foo = FooImpl() } Component

Slide 72

Slide 72 text

Scope Annotation , Component . @Module @InstallIn(SingletonComponent::class) object ExampleModule { // unscoped @Provides fun providesBar(foo: Foo): Bar = BarImpl(foo) @Singleton @Provides fun providesFoo(): Foo = FooImpl() } Scope

Slide 73

Slide 73 text

Component , . Component hierarchy

Slide 74

Slide 74 text

Component hierarchy @Module @InstallIn(SingletonComponent::class) object AppModule { @Provides fun providesFoo(): Foo = FooImpl() } @Module @InstallIn(ActivityComponent::class) object ActivityModule { @Provides fun providesBar(foo: Foo): Bar = BarImpl(foo) }

Slide 75

Slide 75 text

Component hierarchy @Module @InstallIn(SingletonComponent::class) object AppModule { @Provides fun providesBaz(bar: Bar): Baz = BazImpl(bar) } @Module @InstallIn(ActivityComponent::class) object ActivityModule { @Provides fun providesBar(foo: Foo): Bar = BarImpl(foo) }

Slide 76

Slide 76 text

SingletonComponent Overview @Module Foo ActivityRetainedComponent Bar ActivityComponent @Module Baz

Slide 77

Slide 77 text

Jetpack Hilt Jetpack . @EntryPoint . ViewModel WorkManager Navigation

Slide 78

Slide 78 text

ViewModel @HiltViewModel . , . @HiltViewModel class ExampleViewModel @Inject constructor( private val repository: ExampleRepository, ) : ViewModel() { ... } @AndroidEntryPoint class ExampleActivity : Activity() { private val exampleViewModel: ExampleViewModel by viewModels() } Jetpack : ViewModel

Slide 79

Slide 79 text

@HiltWorker, @AssistedInject , Worker . @Assisted . @HiltWorker class ExampleWorker @AssistedInject constructor( @Assisted appContext: Context, @Assisted workerParams: WorkerParameters, private val repository: ExampleRepository, ) : Worker(appContext, workerParams) { ... } Jetpack : WorkManager

Slide 80

Slide 80 text

// ۄ੉࠳۞ܻܳ ୶о ࢸ஖೧ঠ ೠ׮. dependencies { implementation 'androidx.hilt:hilt-work:1.0.0' kapt 'androidx.hilt:hilt-compiler:1.0.0' } // HiltWorkerFactory۽ WorkManagerܳ ୡӝച೧ঠ ೠ׮. @HiltAndroidApp class ExampleApplication : Application(), Configuration.Provider { @Inject lateinit var workerFactory: HiltWorkerFactory override fun getWorkManagerConfiguration() = Configuration.Builder() .setWorkerFactory(workerFactory) .build() }

Slide 81

Slide 81 text

Dagger Hilt Hilt . @HiltAndroidApp class ExampleApplication : Application() { @Inject lateinit var bar: Bar } @Module @InstallIn(SingletonComponent::class) object FooModule { @Provides fun provideBar(): Bar = Bar() }

Slide 82

Slide 82 text

Dagger Hilt Bar ExampleApplication DaggerExampleApplication_HiltComponents_SingletonC FooModule ੄ઓࢿ ஶప੉ց

Slide 83

Slide 83 text

Dagger Hilt (1) . @HiltAndroidApp class ExampleApplication : Application() { @Inject lateinit var bar: Bar } @Module @InstallIn(SingletonComponent::class) object FooModule { @Provides fun provideBar(): Bar = Bar() }

Slide 84

Slide 84 text

@HiltAndroidApp class ExampleApplication : Application() // -------------------------------------------------------------------------------- // Generated by Hilt public final class ExampleApplication_HiltComponents { @Component(modules = { ActivityRetainedCBuilderModule.class, ... }) @Singleton public abstract static class SingletonC implements SingletonComponent { } ... } // -------------------------------------------------------------------------------- // Generated by Dagger public final class DaggerExampleApplication_HiltComponents_SingletonC { // ੄ઓࢿ ஶప੉ց private static final class SingletonCImpl extends ExampleApplication_HiltComponents.SingletonC { public void injectExampleApplication(ExampleApplication exampleApplication) {} } ... } @HiltAndroidApp - ੄ઓࢿ ஶప੉ցܳ ࢤࢿೠ׮.

Slide 85

Slide 85 text

Dagger Hilt (2) . @HiltAndroidApp class ExampleApplication : Application() { @Inject lateinit var bar: Bar } @Module @InstallIn(SingletonComponent::class) object FooModule { @Provides fun provideBar(): Bar = Bar() }

Slide 86

Slide 86 text

ࢶ঱ೠ Componentী Module੉ ୶оػ׮. @Module @InstallIn(SingletonComponent::class) object FooModule { @Provides fun provideBar(): Bar = Bar() } // -------------------------------------------------------------------------------- public final class ExampleApplication_HiltComponents { @Component( modules = { ApplicationContextModule.class, ActivityRetainedCBuilderModule.class, FooModule.class, } ) @Singleton public abstract static class SingletonC implements HiltWrapper_ActivityRetainedComponentManager_ActivityRetainedComponentBuilderEntryPoint, SingletonComponent, GeneratedComponent { } }

Slide 87

Slide 87 text

@Provides݃׮ ੄ઓࢿਸ ઁҕೞח Factory ௿ېझо ࢤࢿػ׮. @Module @InstallIn(SingletonComponent::class) object FooModule { @Provides fun provideBar(): Bar = Bar() } // -------------------------------------------------------------------------------- public final class FooModule_ProvideBarFactory implements Factory { public static Bar provideBar() { return Preconditions.checkNotNullFromProvides(FooModule.INSTANCE.provideBar()); } }

Slide 88

Slide 88 text

Dagger Hilt (3) . @HiltAndroidApp class ExampleApplication : Application() { @Inject lateinit var bar: Bar } @Module @InstallIn(SingletonComponent::class) object FooModule { @Provides fun provideBar(): Bar = Bar() } Hilt_ExampleApplication.java ExampleApplication_GeneratedInjector.java ExampleApplication_MembersInjector.java

Slide 89

Slide 89 text

@HiltAndroidApp class ExampleApplication : Application() { @Inject lateinit var bar: Bar } = @HiltAndroidApp class ExampleApplication : Hilt_ExampleApplication() { @Inject lateinit var bar: Bar } ߄੉౟௏٘ܳ ߸ജೞৈ, ࠗݽ ௿ېझܳ ߸҃ೠ׮.

Slide 90

Slide 90 text

Hilt_ExampleApplication.java public abstract class Hilt_ExampleApplication extends Application { private boolean injected = false; @CallSuper @Override public void onCreate() { if (!injected) { injected = true; ((ExampleApplication_GeneratedInjector) componentManager.generatedComponent()) .injectExampleApplication(this); } super.onCreate(); } private final ApplicationComponentManager componentManager = new ApplicationComponentManager( () -> DaggerExampleApplication_HiltComponents_SingletonC.builder() .applicationContextModule(new ApplicationContextModule(Hilt_ExampleApplication.this)) .build()); } Injector۽ SingletonCী ੽Ӕೞৈ, ੄ઓࢿ ઱ੑਸ ਃ୒ೠ׮.

Slide 91

Slide 91 text

ExampleApplication_GeneratedInjector.java @GeneratedEntryPoint @InstallIn(SingletonComponent.class) public interface ExampleApplication_GeneratedInjector { void injectExampleApplication(ExampleApplication exampleApplication); } // -------------------------------------------------------------------------------- public final class ExampleApplication_HiltComponents { @Component(modules = { ... }) @Singleton public abstract static class SingletonC implements HiltWrapper_ActivityRetainedComponentManager_ActivityRetainedComponentBuilderEntryPoint, SingletonComponent, GeneratedComponent, ExampleApplication_GeneratedInjector { } } // -------------------------------------------------------------------------------- public final class DaggerExampleApplication_HiltComponents_SingletonC { private static final class SingletonCImpl extends SingletonC { public void injectExampleApplication(ExampleApplication instance) { } }

Slide 92

Slide 92 text

ExampleApplication_MembersInjector.java @HiltAndroidApp class ExampleApplication : Application() { @Inject lateinit var bar: Bar } // -------------------------------------------------------------------------------- public final class ExampleApplication_MembersInjector implements MembersInjector { public static void injectBar(ExampleApplication instance, Bar bar) { instance.bar = bar; } } // -------------------------------------------------------------------------------- public final class DaggerExampleApplication_HiltComponents_SingletonC { private static final class SingletonCImpl extends SingletonC { public void injectExampleApplication(ExampleApplication instance) { ExampleApplication_MembersInjector.injectBar( instance, FooModule_ProvideBarFactory.provideBar()); } } ࢤࢿػ Injector ௿ېझܳ ా೧, উ٘۽੉٘ ௿ېझী ੄ઓࢿ੉ ઱ੑػ׮.

Slide 93

Slide 93 text

Dagger Hilt DaggerExampleApplication_HiltComponents_SingletonC SingletonCImpl FooModule_ProvideBarFactory ExampleApplication_MemeberInjector ExampleApplication Hilt_ExampleApplication Bar ExampleApplication_GeneratedInjector ApplicationComponentManager

Slide 94

Slide 94 text

Dagger Hilt Dagger Hilt . ( ) Link: h tt ps://speakerdeck.com/fornewid, h tt ps://youtu.be/iE_2llWUI6A

Slide 95

Slide 95 text

Dagger Hilt 4

Slide 96

Slide 96 text

Component Component , . SingletonComponent (1) ActivityRetainedComponent ActivityComponent FragmentComponent (3) (2)

Slide 97

Slide 97 text

Before class ExampleActivity : ComponentActivity() { private val bar = Bar() bar.doSomething() }

Slide 98

Slide 98 text

@AndroidEntryPoint class ExampleActivity : ComponentActivity() { @Inject lateinit var bar: Bar bar.doSomething() } @Module @InstallIn(SingletonComponent::class) object BarModule { @Provides fun provideBar(): Bar = Bar() }

Slide 99

Slide 99 text

: Singleton Singleton . object SingletonBar { fun doSomething() { ... } } ... SingletonBar.doSomething()

Slide 100

Slide 100 text

Before class ExampleActivity : ComponentActivity() { SingletonBar.doSomething() }

Slide 101

Slide 101 text

, Singleton @AndroidEntryPoint class ExampleActivity : ComponentActivity() { @Inject lateinit var singletonBar: SingletonBar singletonBar.doSomething() } @Module @InstallIn(SingletonComponent::class) object BarModule { @Provides fun provideBar(): SingletonBar = SingletonBar }

Slide 102

Slide 102 text

interface-impl , Singleton interface SingletonBar { fun doSomething() } @Deprecated object SingletonBarImpl : SingletonBar { override fun doSomething() { ... } } @Module @InstallIn(SingletonComponent::class) object BarModule { @Provides fun provideBar(): SingletonBar = SingletonBarImpl }

Slide 103

Slide 103 text

Hilt , Singleton interface SingletonBar { fun doSomething() } @Singleton class SingletonBarImpl @Inject constructor() : SingletonBar { override fun doSomething() { ... } } @Module @InstallIn(SingletonComponent::class) interface BarModule { @Binds fun bindBar(impl: SingletonBarImpl): SingletonBar }

Slide 104

Slide 104 text

: Singleton + Context Singleton Context . class SingletonBar private constructor(private val context: Context) { fun doSomething() { ... } companion object { private lateinit var instance: SingletonBar fun initialize(context: Context) { instance = SingletonBar(context.applicationContext) } fun getInstance(): SingletonBar = instance } }

Slide 105

Slide 105 text

Before class ExampleApplication : Application() { override fun onCreate() { super.onCreate() SingletonBar.initialize(this) } } class ExampleActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) SingletonBar.getInstance().doSomething() } }

Slide 106

Slide 106 text

, Singleton @Module @InstallIn(SingletonComponent::class) object BarModule { @Provides fun provideBar(): SingletonBar = SingletonBar.getInstance() } @AndroidEntryPoint class ExampleActivity : ComponentActivity() { @Inject lateinit var singletonBar: SingletonBar override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) singletonBar.doSomething() } }

Slide 107

Slide 107 text

interface-impl , Singleton @Module @InstallIn(SingletonComponent::class) object BarModule { @Provides fun provideBar(): SingletonBar = SingletonBarImpl.getInstance() } interface SingletonBar { fun doSomething() } class SingletonBarImpl private constructor( context: Context ) : SingletonBar { ... }

Slide 108

Slide 108 text

Hilt , Singleton @Module @InstallIn(SingletonComponent::class) interface BarModule { @Binds fun bindBar(impl: SingletonBarImpl): SingletonBar } interface SingletonBar { fun doSomething() } @Singleton class SingletonBarImpl @Inject constructor( @ApplicationContext context: Context ) : SingletonBar { ... }

Slide 109

Slide 109 text

: Singleton + Singleton Singleton . class SingletonBar private constructor(...) { fun doSomething() { SingletonFoo.doSomething() } } class SingletonFoo private constructor(...) { fun doSomething() { ... } }

Slide 110

Slide 110 text

Before class ExampleActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) SingletonBar.getInstance().doSomething() } } class SingletonBar private constructor(...) { fun doSomething() { SingletonFoo.getInstance().doSomething() } } class SingletonFoo private constructor(...) { fun doSomething() { ... } }

Slide 111

Slide 111 text

Before class SingletonBar private constructor(private val context: Context) { fun doSomething() { SingletonFoo.getInstance().doSomething() } } class SingletonFoo private constructor( private val context: Context ) { fun doSomething() { ... } }

Slide 112

Slide 112 text

After class SingletonBar private constructor(private val context: Context) { private val singletonFoo: SingletonFoo fun doSomething() { singletonFoo.doSomething() } } @Singleton class SingletonFoo @Inject constructor( @ApplicationContext private val context: Context ) { fun doSomething() { ... } }

Slide 113

Slide 113 text

After class SingletonBar private constructor(private val context: Context) { private val singletonFoo: SingletonFoo init { val entryPoint = EntryPointAccessors.fromApplication( context.applicationContext, SingletonBarEntryPoint::class.java ) singletonFoo = entryPoint.provideFoo() } } @EntryPoint @InstallIn(SingletonComponent::class) interface SingletonBarEntryPoint { fun provideFoo(): SingletonFoo }

Slide 114

Slide 114 text

Final @Singleton class SingletonBar @Inject constructor( private val context: Context, private val singletonFoo: SingletonFoo, ) { fun doSomething() { singletonFoo.doSomething() } }

Slide 115

Slide 115 text

Depth . @AndroidEntryPoint class ExampleActivity : ComponentActivity() { @Inject lateinit var singletonBar: SingletonBar fun doSomething() { singletonBar.doSomething() } } (3) (1) (2) app start

Slide 116

Slide 116 text

: Component Component Component @AndroidEntryPoint . , Fragment Hilt Fragment Activity @AndroidEntryPoint . @AndroidEntryPoint class ExampleActivity : ComponentActivity() { ... } @AndroidEntryPoint class ExampleFragment : Fragment() { ... } ExampleActivity ExampleFragment @inject

Slide 117

Slide 117 text

ViewModel @HiltViewModel class ExampleViewModel @Inject constructor( private val singletonBar: SingletonBar, ) : ViewModel() { ... } @AndroidEntryPoint class ExampleActivity : ComponentActivity() { private val viewModel: ExampleViewModel by viewModels() } class ExampleFragment : ComponentActivity() { }

Slide 118

Slide 118 text

ViewModel @HiltViewModel class ExampleViewModel @Inject constructor( private val singletonBar: SingletonBar, ) : ViewModel() { ... } @AndroidEntryPoint class ExampleActivity : ComponentActivity() { // private val viewModel: ExampleViewModel by viewModels() } @AndroidEntryPoint class ExampleFragment : ComponentActivity() { private val viewModel: ExampleViewModel by viewModels() }

Slide 119

Slide 119 text

ViewModel @HiltViewModel class ExampleViewModel @Inject constructor( private val singletonBar: SingletonBar, ) : ViewModel() { ... } @AndroidEntryPoint class ExampleActivity : ComponentActivity() { ... } class ExampleFragment : ComponentActivity() { private val viewModel: ExampleViewModel by activityViewModels() }

Slide 120

Slide 120 text

: Base Base , @AndroidEntryPoint . class BaseActivity : ComponentActivity() { @Inject lateinit var bar: Bar } @AndroidEntryPoint class ExampleActivity : BaseActivity() { ... }

Slide 121

Slide 121 text

, ? 🤔

Slide 122

Slide 122 text

#1: @AndroidEntryPoint . , . @AndroidEntryPoint class ExampleActivity : AppCompatActivity() { @Inject lateinit var bar: Bar override fun attachBaseContext(base: Context) { super.attachBaseContext(bar.newContext(base)) } } Link: h tt ps://stackove rf low.com/a/64162926

Slide 123

Slide 123 text

#2: Generic Base Generic , . @AndroidEntryPoint class ExampleActivity : BaseActivity() { ... } Link: h tt ps://github.com/google/dagger/issues/3370

Slide 124

Slide 124 text

Dagger Hilt 5 ⚠ : = Gradle Module / @Module = Dagger Module

Slide 125

Slide 125 text

App Modularization , . :app:android :feature:home :data:books :feature:reviews :feature:player :app:auto :core:network :data:reviews

Slide 126

Slide 126 text

App Modularization: @Module Hilt Module , . :app:android :feature:home :data:books :feature:reviews :feature:player :app:auto :core:network :data:reviews @Module @Module @Module

Slide 127

Slide 127 text

App Modularization: @Module @HiltAndroidApp Hilt Module . :app:android :feature:home :data:books :core:network @Module @InstallIn(SingletonComponent::class) object FeatureHomeModule { ... } @Module @InstallIn(SingletonComponent::class) object DataBooksModule { ... } @Module @InstallIn(SingletonComponent::class) object CoreNetworkModule { ... } @HiltAndroidApp class MyApp : Application()

Slide 128

Slide 128 text

Without Classpath Aggregation @Module . hilt_metadata :app:android :feature:home :data:books :core:network implementation implementation api @Metadata @Metadata @InstallIn @InstallIn @InstallIn @Generated("dagger.hilt") interface MyApp_HiltComponents { @Component(modules = [ FeatureHomeModule::class, DataBooksModule::class, ... ]) interface SingletonC } CoreNetworkModule::class, Link: h tt ps://medium.com/androiddevelopers/mad-skills-series-hilt-under-the-hood-9d89ee227059

Slide 129

Slide 129 text

With Classpath Aggregation Classpath Aggregation , . hilt_metadata :app:android :feature:home :data:books :core:network implementation implementation api @Metadata @Metadata @Metadata @InstallIn @InstallIn @InstallIn @Generated("dagger.hilt") interface MyApp_HiltComponents { @Component(modules = [ FeatureHomeModule::class, DataBooksModule::class, ... ]) interface SingletonC } CoreNetworkModule::class, Link: h tt ps://medium.com/androiddevelopers/mad-skills-series-hilt-under-the-hood-9d89ee227059

Slide 130

Slide 130 text

App Architecture . UI Layer Domain Layer Data Layer

Slide 131

Slide 131 text

App Architecture . :ui :domain :data implementation implementation

Slide 132

Slide 132 text

App Architecture Hilt , . :data class ExampleUseCase @Inject constructor( private val repository: ExampleRepository ) { ... } :ui :domain class ExampleRepository @Inject constructor( ) { ... }

Slide 133

Slide 133 text

App Architecture: interface, class , :domain :data class ExampleUseCase @Inject constructor( private val repository: ExampleRepository ) { ... } class ExampleRepository @Inject constructor( ) { ... }

Slide 134

Slide 134 text

App Architecture: interface, class interface class , . :domain :data interface ExampleRepository class ExampleRepositoryImpl @Inject constructor( ) : ExampleRepository { ... } @Module @InstallIn(SingletonComponent::class) interface DataModule { @Binds fun bindRepository( impl: ExampleRepositoryImpl ) : ExampleRepository } class ExampleUseCase @Inject constructor( private val repository: ExampleRepository ) { ... }

Slide 135

Slide 135 text

App Architecture: internal , internal . :domain :data interface ExampleRepository internal class ExampleRepositoryImpl @Inject constructor( ) : ExampleRepository { ... } @Module @InstallIn(SingletonComponent::class) internal interface DataModule { @Binds fun bindRepository( impl: ExampleRepositoryImpl ) : ExampleRepository } class ExampleUseCase @Inject constructor( private val repository: ExampleRepository ) { ... }

Slide 136

Slide 136 text

internal @Module , HiltWrapper internal @Module @OriginatingElement(topLevelClass = DataModule.class) @InstallIn(SingletonComponent.class) @Module(includes = DataModule.class) public final class HiltWrapper_DataModule {} @Module @InstallIn(SingletonComponent::class) internal interface DataModule { @Binds fun bindRepository(impl: ExampleRepositoryImpl) : ExampleRepository }

Slide 137

Slide 137 text

internal @Module ؀न Componentী ୶о೧ળ׮. Ӓ۞ա… public final class ExampleApplication_HiltComponents { @Component( modules = { ApplicationContextModule.class, ActivityRetainedCBuilderModule.class, HiltWrapper_DataModule.class, } ) @Singleton public abstract static class SingletonC implements HiltWrapper_ActivityRetainedComponentManager_ActivityRetainedComponentBuilderEntryPoint, SingletonComponent, GeneratedComponent { } }

Slide 138

Slide 138 text

App Architecture: internal internal . ( ) = . ( ) =

Slide 139

Slide 139 text

App Architecture: interface class . :domain-qux :domain-bar-api :domain-baz :domain-bar-impl interface Bar class BarImpl(foo: Foo) : Bar interface Qux class QuxImpl(bar: Bar) : Qux interface Baz class BazImpl(bar: Bar) : Baz

Slide 140

Slide 140 text

App Architecture: @Module . :domain-qux :domain-bar-api :domain-baz :domain-bar-impl interface Bar class BarImpl(foo: Foo) : Bar -> interface Qux class QuxImpl(bar: Bar) : Qux @Module interface Baz class BazImpl(bar: Bar) : Baz

Slide 141

Slide 141 text

App Architecture: (DIP) . :domain-qux-impl :domain-baz-impl :domain-bar-impl :domain-foo-impl class BazImpl(bar: Bar) : Baz class BarImpl(foo: Foo) : Bar class FooImpl() : Foo class QuxImpl(baz: Baz) : Qux :domain-qux-api :domain-baz-api :domain-bar-api :domain-foo-api interface Baz interface Bar interface Foo interface Qux Link: h tt ps://en.wikipedia.org/wiki/Dependency_inversion_principle

Slide 142

Slide 142 text

App Modularization: Final! . :app:android :feature:home-api :data:books-api :data:books-impl :app:android :feature:home :data:books :feature:home-impl Before After implementation implementation

Slide 143

Slide 143 text

App Modularization: Final! (+ runtimeOnly) , . :app:android :feature:home-api :data:books-api :data:books-impl runtimeOnly :app:android :feature:home :data:books :feature:home-impl runtimeOnly Before After

Slide 144

Slide 144 text

, . , DI . Dagger Hilt . Component, Scope, Module, @Inject, @AndroidEntryPoint, … . Dagger Hilt , .

Slide 145

Slide 145 text

@Module @Provides @Binds @Inject @AndroidEntryPoint @EntryPoint @HiltAndroidApp @Qualifier

Slide 146

Slide 146 text

SingletonComponent @Module Foo ActivityRetainedComponent Bar ActivityComponent @Module Baz

Slide 147

Slide 147 text

!