$30 off During Our Annual Pro Sale. View Details »

Dagger Hilt로 의존성 주입하기

Dagger Hilt로 의존성 주입하기

2023년 7월 29일 (토) I/O Extended 2023 Seoul 행사에서의 발표자료입니다.
https://festa.io/events/3683

Sungyong An

July 29, 2023
Tweet

More Decks by Sungyong An

Other Decks in Programming

Transcript

  1. Dagger Hilt۽

    ੄ઓࢿ ઱ੑೞӝ
    Android

    View Slide

  2. Sungyong An

    NAVER WEBTOON

    Android GDE

    @fornewid

    View Slide

  3. Intro
    “৵ ૑Ә Dagger Hiltܳ…?”

    “Dagger Hiltী ࢜۽਍ ߸҃ࢎ೦੉ ੓աਃ?”

    “׮ܲ ੄ઓࢿ ઱ੑ ۄ੉࠳۞ܻ৬ ࠺Үೞח ղਊੋоਃ?”
    🤔

    View Slide

  4. Intro
    “৵ ૑Ә Dagger Hiltܳ…?”

    “Dagger Hiltী ࢜۽਍ ߸҃ࢎ೦੉ ੓աਃ?”

    “׮ܲ ੄ઓࢿ ઱ੑ ۄ੉࠳۞ܻ৬ ࠺Үೞח ղਊੋоਃ?”
    🤔

    View Slide

  5. Intro
    ੄ઓࢿ ઱ੑ(DI)਷ ೞա੄ ё୓о ׮ܲ ё୓੄ ੄ઓࢿਸ ઁҕೞח ӝߨ੉׮.

    ੄ઓࢿ ઱ੑ੄ ੄بח ё୓੄ ࢤࢿҗ ࢎਊ੄ ҙबਸ ܻ࠙ೞח Ѫ੉׮.
    Dependency Injection?
    Link: https://en.wikipedia.org/wiki/Dependency_injection

    View Slide

  6. // Without Dependency Injection


    class Car {


    private val engine: Engine = Engine()


    fun start() {


    engine.start()


    }


    }


    fun main(args: Array) {


    val car = Car()


    car.start()


    }
    ੄ઓࢿ ઱ੑ হ੉, Car ௿ېझ ղࠗীࢲ Engineਸ ࢤࢿೞח ௏٘ੑפ׮.

    View Slide

  7. // Dependency Injection (Constructor Injection)


    class Car(


    private val engine: Engine


    ) {


    fun start() {


    engine.start()


    }


    }


    fun main(args: Array) {


    val engine = Engine()


    val car = Car(engine)


    car.start()


    }
    ੄ઓࢿ ઱ੑীࢲח Car ௿ېझ ࢤࢿ੗ী Engineਸ ੹׳ೞѢա,

    View Slide

  8. // Dependency Injection (Field Injection)


    class Car {


    lateinit var engine: Engine


    fun start() {


    engine.start()


    }


    }


    fun main(args: Array) {


    val car = Car()


    car.engine = Engine()


    car.start()


    }
    ৻ࠗীࢲ Engineਸ ࢤࢿೞৈ ೙٘ ઱ੑਵ۽ ੹׳೤פ׮.

    View Slide

  9. Intro
    DI੄ ਗ஗ਸ ٮܰݶ ഴܯೠ জ ইఃఫ୊ܳ ਤೠ ష؀ܳ ݃۲ೡ ࣻ ੓׮.

    • ௏٘ ੤ࢎਊ оמ

    • ܻಂష݂ ಞ੄ࢿ

    • పझ౟ ಞ੄ࢿ
    Dependency Injection ੉੼?
    Link: https://en.wikipedia.org/wiki/Dependency_injection

    View Slide

  10. Intro
    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
    ࢶఖೠ ੉ਬ? ૑ࣘоמࢿ

    View Slide

  11. Intro
    • ё୓ܳ ࢤࢿೞח Ҕҗ ࢎਊೞח Ҕ੉ ܻ࠙غয, ௏٘ܳ ୶੸ೞӝ য۵ѱ ݅ٚ׮.

    • ✅ Android Studioח Dagger ߂ Hilt ҙ۲ ௏٘ р੄ ఐ࢝ਸ ૑ਗೠ׮.

    https://medium.com/androiddevelopers/dagger-navigation-support-in-android-studio-49aa5d149ec9

    • ௏٘о ࠗ࠙ࠗ࠙ ܻ࠙غয, दझమ ੹୓о ࣻ೯ೞח ੘সਸ ౵ঈೞӝ য۵׮.

    • ࢎਊೞח ౠ੿ DI ೐ۨ੐ਕ௼ী ੄ઓೞѱ ػ׮.
    Dependency Injection == ?
    Link: https://en.wikipedia.org/wiki/Dependency_injection
    💯

    View Slide

  12. Intro
    Java, Kotlin MVC, MVP, MVVM, MVI
    Language Architecture
    Java Threads, AsyncTask, HandlerThread,

    Guava, RxJava, Kotlin Coroutines
    Asynchronous work
    View, Layout XML, anko, Litho, Compose,

    ButterKnife, Kotlin Android Extensions,

    DataBinding, ViewBinding
    Android Universal Image Loader, Picasso,

    Glide, Coil, Volley, OkHttp, Retrofit, Ktor,

    Jackson, Gson, Moshi, kotlinx.serialization
    User Interface Network Background work
    Service, AlarmManager, JobScheduler,

    GCMNetworkManager, android-job,

    JobDispatcher, JobService, WorkManager

    View Slide

  13. Intro
    Java, Kotlin MVC, MVP, MVVM, MVI
    Language Architecture
    Java Threads, AsyncTask, HandlerThread,

    Guava, RxJava, Kotlin Coroutines
    Asynchronous work
    View, Layout XML, anko, Litho, Compose,

    ButterKnife, Kotlin Android Extensions,

    DataBinding, ViewBinding
    Android Universal Image Loader, Picasso,

    Glide, Coil, Volley, OkHttp, Retrofit, Ktor,

    Jackson, Gson, Moshi, kotlinx.serialization
    User Interface Network Background work
    Service, AlarmManager, JobScheduler,

    GCMNetworkManager, android-job,

    JobDispatcher, JobService, WorkManager
    Nothing Lasts Forever

    View Slide

  14. “Hiltܳ ੉ਊೞৈ Android জী ੄ઓࢿ ઱ੑ ಁఢਸ ҳഅೞغ,

    ݾ੸ਸ ஂೡ ݅ఀ݅ ੸׼൤ ࢎਊೞח ߑߨਸ Ҋ޹೧ ࠁ੗.”
    ݾ಴

    View Slide

  15. Hiltо ઁҕೞח জী ੄ઓࢿ ઱ੑೞח ߑߨਸ ঌইࠄ׮.

    Jetpack ۄ੉࠳۞ܻ৬ োزغח ࠗ࠙ب рױ൤ ࢓ಝࠄ׮.
    ݾ੸ী ݏѱ, জীࢲ Hiltܳ ࢎਊೞח ഋకܳ Ҋ޹೧ ࠄ׮.

    ӝࠄ о੉٘ࠁ׮ ؊ ࠂ੟ೞѱ ࢎਊೞח Ѫ੉ ա਷ ࠗ࠙җ

    য়൤۰ ࢎਊೞ૑ ঋח Ѫ੉ ա਷ ࠗ࠙ ١ਸ ࢤп೧ ࠄ׮.
    Dagger Hilt Thinking Usages
    ݾର

    View Slide

  16. Dependency Injection

    with Dagger Hilt
    Section #1
    Dagger Hiltܳ ੉ਊೞৈ ੄ઓࢿ ઱ੑೞӝ

    View Slide

  17. // build.gradle


    buildscript {


    repositories {


    mavenCentral()


    }


    dependencies {


    classpath 'com.google.dagger:hilt-android-gradle-plugin:2.47'


    }


    }


    // app/build.gradle


    apply plugin: 'com.google.dagger.hilt.android'


    apply plugin: 'kotlin-kapt'


    dependencies {


    implementation 'com.google.dagger:hilt-android:2.47'


    kapt 'com.google.dagger:hilt-compiler:2.47'


    }
    Link: https://dagger.dev/hilt/gradle-setup
    Gradle Setup

    View Slide

  18. Section #1
    ӝࠄ੸ੋ Dagger Hilt ࢎਊߑߨਸ ࢓ಝࠁ੗.

    • @HiltAndroidApp, @AndroidEntryPoint, @EntryPoint

    • Components

    • SingletonComponent, ViewModelComponent, ActivityComponent, ...

    • Scopes

    • @Singleton, @ViewModelScoped, @ActivityScoped, …

    • Modules

    • @Inject, @Module, @Provides, @Binds, @Qualifier, @Lazy, …
    Hilt Basics
    Link: https://dagger.dev/hilt/quick-start

    View Slide

  19. @HiltAndroidApp


    class ExampleApplication : Application() {


    @Inject lateinit var bar: Bar


    override fun onCreate() {


    super.onCreate()


    // Use bar


    }


    }
    (1) Hiltܳ ࢎਊೞח ݽٚ জ਷ Applicationী HiltAndroidAppਸ ୶о೧ঠ ೤פ׮.

    View Slide

  20. class ExampleApplication : Hilt_ExampleApplication()


    public abstract class Hilt_ExampleApplication extends Application implements ... {


    @CallSuper


    @Override


    public void onCreate() {


    hiltInternalInject();


    super.onCreate();


    }


    ...


    }
    Generated
    Gradle ೒۞Ӓੋਸ ੉ਊೞݶ ࢚ࣘਸ ా೧

    View Slide

  21. public final class ExampleApplication_MembersInjector


    implements MembersInjector {


    private final Provider barProvider;


    public ExampleApplication_MembersInjector(Provider barProvider) {


    this.barProvider = barProvider;


    }


    @Override


    public void injectMembers(ExampleApplication instance) {


    injectBar(instance, barProvider.get());


    }


    public static void injectBar(ExampleApplication instance, Bar bar) {


    instance.bar = bar;


    }


    }
    Generated
    ੄ઓࢿب ઱ੑೡ ࣻ ੓णפ׮.

    View Slide

  22. @AndroidEntryPoint


    class ExampleActivity : Activity() {


    @Inject lateinit var bar: Bar


    override fun onCreate() {


    super.onCreate()


    // Use bar


    }


    }
    (2) Activityח AndroidEntryPointܳ ੉ਊೞৈ, Field Injection੉ оמ೤פ׮.

    View Slide

  23. class ExampleActivity : Hilt_ExampleActivity()


    public abstract class Hilt_ExampleActivity extends Activity implements ... {


    Hilt_ExampleActivity() {


    super();


    _initHiltInternal();


    }


    private void _initHiltInternal() {


    addOnContextAvailableListener(new OnContextAvailableListener() {


    @Override


    public void onContextAvailable(Context context) {


    inject();


    }


    });


    }


    }
    Generated
    ݃ଲо૑۽ ࢚ࣘਸ ా೧, ੄ઓࢿਸ ઱ੑ೤פ׮.

    View Slide

  24. @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() { ... }
    Fragment, View, Service, BroadcastReceiverب زੌ೤פ׮.

    View Slide

  25. public abstract class Hilt_ExampleFragment extends Fragment


    class ExampleFragment : Hilt_ExampleFragment() { ... }


    public abstract class Hilt_ExampleView extends View


    class ExampleView @JvmOverloads constructor(


    context: Context,


    attrs: AttributeSet? = null,


    defStyleAttr: Int = 0,


    ) : Hilt_ExampleView(context, attrs, defStyleAttr) { ... }


    public abstract class Hilt_ExampleService extends Service


    class ExampleService : Hilt_ExampleService() { ... }


    public abstract class Hilt_ExampleBroadcastReceiver extends BroadcastReceiver


    class ExampleBroadcastReceiver : Hilt_ExampleBroadcastReceiver() { ... }
    Generated
    ݃ଲо૑۽ ࢚ࣘਸ ా೧, ੄ઓࢿਸ ઱ੑ೤פ׮.

    View Slide

  26. class ExampleContentProvider : ContentProvider() {


    @EntryPoint


    @InstallIn(SingletonComponent::class)


    interface ExampleContentProviderEntryPoint {


    fun bar(): Bar


    }


    override fun query(...): Cursor {


    val appContext = context?.applicationContext ?: throw IllegalStateException()


    val hiltEntryPoint = EntryPointAccessors.fromApplication(


    appContext,


    ExampleContentProviderEntryPoint::class.java


    )


    val bar = hiltEntryPoint.bar()


    ...


    }


    }
    (3) ContentProviderח EntryPointܳ ੉ਊೞৈ, ੄ઓࢿਸ ઱ੑೡ ࣻ ੓णפ׮.

    View Slide

  27. @Module


    @InstallIn(SingletonComponent::class)


    object ExampleModule {


    @Provides


    fun providesBar(foo: Foo): Bar = Bar(foo)


    @Provides


    fun providesFoo(): Foo = Foo()


    }
    (4) ઱ੑ೧ঠ ೡ ੄ઓࢿਸ Moduleী ੿੄ೡ ࣻ ੓णפ׮.

    View Slide

  28. @Module


    @InstallIn(SingletonComponent::class)


    object ExampleModule {


    @Provides


    fun providesBar(foo: Foo): Bar = Bar(foo)


    @Provides


    fun providesFoo(): Foo = Foo()


    }
    (5) InstallInਸ ੉ਊೞৈ, Moduleਸ ࢸ஖ೞ۰ח Componentܳ ੿੄೤פ׮.

    View Slide

  29. Section #1
    ׮নೠ উ٘۽੉٘ ҳࢿਃࣗ੄

    ࣻݺ ઱ӝী ੗زਵ۽ ా೤غח

    ӝࠄ ઁҕ Componentо ੓णפ׮.

    п ҳࢿ ਃࣗ੄ ࣻݺী ؀ೠ

    ߄ੋ٬ ߧਤܳ ૑੿ೞחؘ ࢎਊغח

    Scope Annotation੉ ੓णפ׮.
    Component
    Link: https://dagger.dev/hilt/components

    View Slide

  30. @Module


    @InstallIn(SingletonComponent::class)


    object ExampleModule {


    @Provides


    fun providesBar(foo: Foo): Bar = Bar(foo)


    @Provides


    fun providesFoo(): Foo = Foo()


    }
    Foo
    Bar
    (6) Providesܳ ੉ਊೞৈ, ੄ઓࢿਸ ઁҕೡ ࣻ ੓णפ׮.

    View Slide

  31. public final class ExampleModule_ProvidesFooFactory implements Factory {


    @Override


    public Foo get() {


    return providesFoo();


    }


    public static ExampleModule_ProvidesFooFactory create() {


    return InstanceHolder.INSTANCE;


    }


    public static Foo providesFoo() {


    return Preconditions.checkNotNullFromProvides(ExampleModule.INSTANCE.providesFoo());


    }


    private static final class InstanceHolder {


    private static final ExampleModule_ProvidesFooFactory INSTANCE =


    new ExampleModule_ProvidesFooFactory();


    }


    }
    Generated
    пп੄ @Provides ೣࣻ݃׮ Factory ௿ېझо ੗ز ࢤࢿؾפ׮.

    View Slide

  32. public final class ExampleModule_ProvidesBarFactory implements Factory {


    private final Provider fooProvider;


    public ExampleModule_ProvidesBarFactory(Provider fooProvider) {


    this.fooProvider = fooProvider;


    }


    @Override


    public Bar get() {


    return providesBar(fooProvider.get());


    }


    public static ExampleModule_ProvidesBarFactory create(Provider fooProvider) {


    return new ExampleModule_ProvidesBarFactory(fooProvider);


    }


    public static Bar providesBar(Foo foo) {


    return Preconditions.checkNotNullFromProvides(ExampleModule.INSTANCE.providesBar(foo));


    }


    }
    Generated

    View Slide

  33. @Module


    @InstallIn(SingletonComponent::class)


    object ExampleModule {


    @Provides


    fun providesBar(foo: Foo): Bar = BarImpl(foo)


    @Provides


    fun providesFoo(): Foo = FooImpl()


    }
    Foo
    Bar
    FooImpl
    BarImpl
    (7) ੋఠಕ੉झ৬ ҳഅ୓ܳ ܻ࠙ೡ ࣻب ੓णפ׮.

    View Slide

  34. @Module


    @InstallIn(SingletonComponent::class)


    object ExampleModule {


    // unscoped


    @Provides


    fun providesBar(foo: Foo): Bar = BarImpl(foo)


    @Singleton


    @Provides


    fun providesFoo(): Foo = FooImpl()


    }
    (8) Scopeܳ ૑੿ೞݶ, ೧׼ Component੄ ࣻݺ੉ ՘զ ٸө૑ ਬ૑ؾפ׮.

    View Slide

  35. private static final class SingletonCImpl extends ... {


    private Provider providesFooProvider;


    private SingletonCImpl() {


    this.providesFooProvider = DoubleCheck.provider(


    () -> ExampleModule_ProvidesFooFactory.providesFoo()


    );


    }


    public Bar providesBar() {


    return ExampleModule_ProvidesBarFactory


    .providesBar((Foo)this.providesFooProvider.get())


    .bar();


    }


    }
    Generated
    Scopeо ૑੿غ૑ ঋਵݶ, ݒߣ ࢤࢿ೤פ׮.

    View Slide

  36. @Module


    @InstallIn(SingletonComponent::class)


    interface ExampleModule {


    @Binds


    fun bindsBar(impl: BarImpl): Bar


    @Singleton


    @Binds


    fun bindsFoo(impl: FooImpl): Foo


    }
    Foo
    Bar
    FooImpl
    BarImpl
    (9) Inject৬ Bindsܳ ੉ਊೞৈ, ҳഅ୓ܳ ੋఠಕ੉झ ഋక۽ ઁҕೡ ࣻب ੓णפ׮.
    class BarImpl @Inject constructor(private val foo: Foo) : Bar { ... }


    class FooImpl @Inject constructor() : Foo { ... }

    View Slide

  37. private static final class SingletonCImpl extends ... {


    private Provider fooImplProvider;


    private Provider bindsFooProvider;


    private SingletonCImpl() {


    this.fooImplProvider = () -> new FooImpl();


    this.bindsFooProvider = DoubleCheck.provider(this.fooImplProvider);


    }


    public Bar providesBar() {


    return new BarImpl((Foo) this.bindsFooProvider.get());


    }


    }
    Generated
    Provides৬ ׮ܰѱ, Bindsח Factory ௿ېझо ࢤࢿغ૑ ঋणפ׮.

    View Slide

  38. Section #1
    Dagger Hilt۽ ViewModelী ੄ઓࢿਸ ઱ੑೡ ࣻ ੓णפ׮.
    Dagger Hilt۽ Workerী ੄ઓࢿਸ ઱ੑೡ ࣻ ੓णפ׮.
    ViewModel WorkManager
    With Jetpack Libraries

    View Slide

  39. @HiltViewModel


    class ExampleViewModel @Inject constructor(


    private val savedStateHandle: SavedStateHandle,


    private val repository: ExampleRepository


    ) : ViewModel() {


    ...


    }


    @AndroidEntryPoint


    class ExampleActivity : Activity() {


    private val exampleViewModel: ExampleViewModel by viewModels()


    ...


    }
    ViewModel
    Activityীࢲ ੄ઓࢿ ઱ੑػ ViewModelী ੽Ӕೞח ௏٘ੑפ׮.

    ViewModel਷ AndroidEntryPoint ؀न, HiltViewModelܳ ࢎਊೞৈ ੄ઓࢿਸ ઱ੑ೤פ׮.

    View Slide

  40. public abstract class Hilt_ExampleActivity extends Activity implements ... {


    ...


    @Override


    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {


    return DefaultViewModelFactories


    .getActivityFactory(this, super.getDefaultViewModelProviderFactory());


    }


    }
    Generated
    Activity੄ Factoryܳ Hilt੄ ҳഅ଻۽ overrideೞח ߑधੑפ׮.

    View Slide

  41. public final class DefaultViewModelFactories {


    public static ViewModelProvider.Factory getActivityFactory(


    ComponentActivity activity, ViewModelProvider.Factory delegateFactory) {


    return EntryPoints.get(activity, ActivityEntryPoint.class)


    .getHiltInternalFactoryFactory()


    .fromActivity(activity, delegateFactory);


    }


    @EntryPoint


    @InstallIn(ActivityComponent.class)


    public interface ActivityEntryPoint {


    InternalFactoryFactory getHiltInternalFactoryFactory();


    }


    ...
    Library Internal
    ղࠗ੸ਵ۽ EntryPointܳ ࢎਊ೤פ׮.

    View Slide

  42. @HiltViewModel


    class ExampleViewModel @Inject constructor(


    private val savedStateHandle: SavedStateHandle,


    private val repository: ExampleRepository


    ) : ViewModel() {


    ...


    }


    import androidx.lifecycle.viewmodel.compose.viewModel


    @Composable


    fun MyScreen(


    viewModel: MyViewModel = viewModel()


    ) {


    ...


    }
    Composeীࢲ ੄ઓࢿ ઱ੑػ ViewModelী ੽Ӕೞח ௏٘ੑפ׮.

    View Slide

  43. package androidx.lifecycle.viewmodel.compose


    @Composable


    public inline fun viewModel(


    viewModelStoreOwner: ViewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {


    "No ViewModelStoreOwner was provided via LocalViewModelStoreOwner"


    },


    key: String? = null,


    factory: ViewModelProvider.Factory? = null,


    extras: CreationExtras = if (viewModelStoreOwner is HasDefaultViewModelProviderFactory) {


    viewModelStoreOwner.defaultViewModelCreationExtras


    } else {


    CreationExtras.Empty


    }


    ): VM = viewModelStoreOwner.get(VM::class.java, key, factory, extras)
    Library Internal
    CompositionLocalਸ ੉ਊೞח ߑधੑפ׮.

    View Slide

  44. package androidx.lifecycle.viewmodel.compose


    public object LocalViewModelStoreOwner {


    private val LocalViewModelStoreOwner =


    compositionLocalOf { null }


    public val current: ViewModelStoreOwner?


    @Composable


    get() = LocalViewModelStoreOwner.current


    ?: LocalView.current.findViewTreeViewModelStoreOwner()


    }
    Library Internal
    LocalViewܳ ా೧, ViewModelStoreOwnerী ੽Ӕ೤פ׮.

    View Slide

  45. package androidx.activity;


    public class ComponentActivity


    extends androidx.core.app.ComponentActivity


    implements ViewModelStoreOwner {


    private void initViewTreeOwners() {


    ...


    ViewTreeViewModelStoreOwner.set(getWindow().getDecorView(), this);


    ...


    }


    }
    Library Internal
    ѾҴ ViewModelStoreOwnerܳ ੉ਊೞৈ,

    View Slide

  46. private fun ViewModelStoreOwner.get(


    javaClass: Class,


    key: String? = null,


    factory: ViewModelProvider.Factory? = null,


    extras: CreationExtras = ...


    ): VM {


    val provider = if (factory != null) {


    ViewModelProvider(this.viewModelStore, factory, extras)


    } else if (this is HasDefaultViewModelProviderFactory) {


    ViewModelProvider(this.viewModelStore, this.defaultViewModelProviderFactory, extras)


    } else {


    ViewModelProvider(this)


    }


    return if (key != null) {


    provider[key, javaClass]


    } else {


    provider[javaClass]


    }


    }
    Library Internal
    Activity੄ Factoryী ੽Ӕ೤פ׮.

    View Slide

  47. // app/build.gradle


    apply plugin: 'kotlin-kapt'


    dependencies {


    implementation 'androidx.hilt:hilt-work:1.0.0'


    kapt 'androidx.hilt:hilt-compiler:1.0.0'


    }
    WorkManager
    (1) WorkManagerח ۄ੉࠳۞ܻܳ ୶о ࢸ஖೧ঠ ೤פ׮.

    View Slide

  48. @HiltWorker


    class ExampleWorker @AssistedInject constructor(


    @Assisted appContext: Context,


    @Assisted workerParams: WorkerParameters,


    bar: Bar,


    ) : CoroutineWorker(appContext, workerParams) {


    override suspend fun doWork(): Result {


    ...


    return Result.success()


    }


    }
    (2) Workerח AndroidEntryPoint ؀न, HiltWorkerܳ ࢎਊ೤פ׮.

    ӒܻҊ Inject ؀न, AssistedInject / Assistedܳ ੉ਊೞৈ ࢤࢿ੗ী ӝࠄ ݒѐ߸ࣻܳ ઱ੑ೤פ׮.

    View Slide

  49. public final class ExampleWorker_Factory {


    private final Provider exampleProvider;


    public ExampleWorker_Factory(Provider exampleProvider) {


    this.exampleProvider = exampleProvider;


    }


    public ExampleWorker get(Context context, WorkerParameters params) {


    return newInstance(context, params, exampleProvider.get());


    }


    ...


    public static ExampleWorker newInstance(Context context, WorkerParameters params,


    ExampleUseCase example) {


    return new ExampleWorker(context, params, example);


    }


    }
    Generated
    ੄ઓࢿਸ ୶оೞח Factory৬

    View Slide

  50. public interface ExampleWorker_AssistedFactory extends WorkerAssistedFactory {}


    public final class ExampleWorker_AssistedFactory_Impl implements ExampleWorker_AssistedFactory {


    private final ExampleWorker_Factory delegateFactory;


    ExampleWorker_AssistedFactory_Impl(ExampleWorker_Factory delegateFactory) {


    this.delegateFactory = delegateFactory;


    }


    @Override


    public ExampleWorker create(Context context, WorkerParameters parameters) {


    return delegateFactory.get(context, parameters);


    }


    public static Provider create(


    ExampleWorker_Factory delegateFactory) {


    return InstanceFactory.create(new ExampleWorker_AssistedFactory_Impl(delegateFactory));


    }


    }
    Generated
    ӝࠄ ݒѐ߸ࣻܳ ୶оೞח Factory. 2о૑о ࢤࢿؾפ׮.

    View Slide

  51. public interface WorkerAssistedFactory {


    @NonNull


    T create(@NonNull Context context, @NonNull WorkerParameters parameters);


    }


    @Module


    @InstallIn(SingletonComponent.class)


    public interface ExampleWorker_HiltModule {


    @Binds


    @IntoMap


    @StringKey("com.example.dagger.hilt.ExampleWorker")


    WorkerAssistedFactory extends ListenableWorker> bind(ExampleWorker_AssistedFactory factory);


    }
    Generated
    Bindsܳ ੉ਊೞৈ, Workerо Mapਵ۽ ޘੑפ׮.

    View Slide

  52. @Module


    @InstallIn(SingletonComponent.class)


    abstract class WorkerFactoryModule {


    @NonNull


    @Multibinds


    abstract Map> workerFactoriesMap();


    @NonNull


    @Provides


    static HiltWorkerFactory provideFactory(@NonNull Map

    Provider>> workerFactories) {


    return new HiltWorkerFactory(workerFactories);


    }


    }
    Generated
    ݽٚ Worker੄ Providerח HiltWorkerFactoryী ੹׳ؾפ׮.

    View Slide

  53. public final class HiltWorkerFactory extends androidx.work.WorkerFactory {


    private final Map

    Provider>> mWorkerFactories;


    ...


    @Nullable


    @Override


    public ListenableWorker createWorker(@NonNull Context appContext,


    @NonNull String workerClassName, @NonNull WorkerParameters workerParameters) {


    Provider> factoryProvider =


    mWorkerFactories.get(workerClassName);


    if (factoryProvider == null) {


    return null;


    }


    return factoryProvider.get().create(appContext, workerParameters);


    }


    }
    Library Internal
    п Providerח Workerܳ पઁ۽ ࢤࢿ೧ঠ ೡ ٸ ࢎਊؾפ׮.

    View Slide

  54. @HiltAndroidApp


    class ExampleApplication : Application(), Configuration.Provider {


    @Inject lateinit var workerFactory: HiltWorkerFactory


    override fun getWorkManagerConfiguration() =


    Configuration.Builder()


    .setWorkerFactory(workerFactory)


    .build()


    }


    val exampleWorkRequest: WorkRequest = OneTimeWorkRequestBuilder().build()


    WorkManager.getInstance(context).enqueue(exampleWorkRequest)
    (3) HiltWorkerFactoryܳ Applicationী ઱ੑ ߉ই, WorkManagerܳ ୡӝചೞݶ ࢸ੿੉ ৮ܐؾפ׮.

    View Slide

  55. Thinking Usages

    about Dagger Hilt
    Section #2
    Dagger Hilt ࢎਊߑߨী ؀೧ ࢤп೧ࠁӝ

    View Slide

  56. Section #2
    WorkManagerח ؀ࠗ࠙੄ ߔӒۄ਍٘ ੘সী
    ӂ੢ೞח ӝࠄ API۽, ౠ൤ Android 14 ࠗఠח
    FGSܳ ؀नೞח ߑߨਵ۽ب ӂ੢ೞҊ ੓׮.

    ੼੼ ؊ ݆੉ ࢎਊೞҊ ੓ח, WorkManagerী
    ੄ઓࢿ ઱ੑೡ ٸ Ҋ޹ೡ݅ೠ ࠗ࠙ਸ ࢓ಝࠁ੗.
    Coroutinesח উ٘۽੉٘ জীࢲ

    ࠺زӝ ୊ܻী ݆੉ ࢎਊغח ߑߨ੉׮.

    ࢎਊೞח ਤ஖ী ٮۄ যڃ CoroutineScopeܳ
    ࢎਊೞח Ѫ੉ ੸੺ೠ૑ Ҋ޹೧ࠁ੗.
    WorkManager Coroutines
    উ٘۽੉٘ জ਷ ӝࠄ ஹನք౟ ৻ীب

    ׮নೠ ۄ੉࠳۞ܻܳ ੉ਊೞৈ ѐߊೠ׮.

    ੉۠ ۄ੉࠳۞ܻܳ ࢎਊೡ ٸ ੄ઓࢿਸ ઱ੑೞח

    ߑߨਵ۽ EntryPoint ࢎਊਸ Ҋ޹೧ࠁ੗.
    EntryPoint
    Thinking Usages

    View Slide

  57. Section #2
    о੉٘ ޙࢲীࢲח ࢸݺೞ૑ ঋח ࠗ࠙ਸ Ҋ޹೧ࠁ੗.

    • WorkManagerܳ ࢎਊೞ۰ݶ ೦࢚ Contextо ೙ਃೞ׮.

    • ࢎਊೡ ࣻ ੓ח ਤ஖о ઁೠغח ޙઁܳ DI۽ ೧Ѿೡ ࣻ ੓׮.

    • WorkManagerח যڃ ࠗ࠙ਸ పझ౟ೞח Ѫ੉ જਸө?

    • Worker੄ ز੘ਸ పझ౟ೠ׮. (X)

    • Worker۽ प೯ೞ۰ח ۽૒ਸ పझ౟ೠ׮. (O)

    • WorkManager ੄ઓࢿਸ ऀѹࢲ, ਗೞח ࠗ࠙݅ਸ పझ౟ೡ ࣻ ੓׮.
    Thinking about WorkManager
    Link: https://developer.android.com/training/dependency-injection/hilt-jetpack#workmanager

    View Slide

  58. // Problem 1:


    val context: Context = ... // Where this from?


    val request = OneTimeWorkRequestBuilder()


    .addTag(ExampleWorker.TAG)


    .build()


    WorkManager.getInstance(context).enqueue(request)
    WorkManagerܳ ࢎਊೞ۰ݶ Contextо ೙ਃೞ׮.
    WorkManager
    Context

    View Slide

  59. @Module


    @InstallIn(SingletonComponent::class)


    object TasksModule {




    @Singleton


    @Provides


    fun provideWorkManager(


    @ApplicationContext context: Context,


    ): WorkManager {


    return WorkManager.getInstance(context)


    }


    }
    DIܳ ੉ਊೞৈ, Context ੄ઓࢿਸ ऀӡ ࣻ ੓Ҋ
    WorkManager

    View Slide

  60. @AndroidEntryPoint


    class ExampleActivity : Activity() {


    @Inject lateinit var workManager: WorkManager


    }


    @HiltViewModel


    class ExampleViewModel @Inject constructor(


    private val workManager: WorkManager


    ) : ViewModel()
    ࢎਊೞ۰ח Ҕী WorkManager۽ ઱ੑ߉ਸ ࣻ ੓णפ׮.
    Example

    ViewModel
    WorkManager
    Example

    Activity

    View Slide

  61. // Problem 2:


    @AndroidEntryPoint


    class ExampleActivity : Activity() {


    @Inject lateinit var workManager: WorkManager


    }


    @HiltViewModel


    class ExampleViewModel @Inject constructor(


    private val workManager: WorkManager


    ) : ViewModel()


    val request = OneTimeWorkRequestBuilder()


    .addTag(ExampleWorker.TAG)


    .build()


    workManager.enqueue(request)
    ೞ૑݅ Worker, WorkManager ١ ೒ۖಬ ௿ېझী ੄ઓࢿਸ ыѱ ؾפ׮.
    WorkManager
    Example

    Activity
    Example

    Worker

    View Slide

  62. @Module


    @InstallIn(SingletonComponent::class)


    interface TasksBindsModule {


    @Binds fun bindsExampleTasks(impl: ExampleTasksImpl): ExampleTasks


    }


    interface ExampleTasks {


    fun execute()


    }


    class ExampleTasksImpl @Inject constructor(


    private val workManager: WorkManager,


    ) : ExampleTasks {


    override fun execute() {


    val request = OneTimeWorkRequestBuilder()


    .addTag(ExampleWorker.TAG)


    .build()


    workManager.enqueue(request)


    }


    }
    DIܳ ੉ਊೞৈ, ز੘਷ زੌೞ૑݅
    WorkManager
    Example

    Worker
    Example

    Tasks

    View Slide

  63. @AndroidEntryPoint


    class ExampleActivity : Activity() {


    @Inject lateinit var exampleTasks: ExampleTasks


    }


    @HiltViewModel


    class ExampleViewModel @Inject constructor(


    private val exampleTasks: ExampleTasks


    ) : ViewModel()


    exampleTasks.execute()
    ೒ۖಬ ௿ېझী ؀ೠ ੄ઓࢿਸ ऀӡ ࣻ ੓णפ׮.
    ExampleTasks
    Example

    ViewModel
    Example

    Activity

    View Slide

  64. // Problem 3:


    @HiltWorker


    class ExampleWorker @AssistedInject constructor(


    @Assisted context: Context,


    @Assisted params: WorkerParameters,


    private val primary: PrimaryDependency,


    private val secondary: SecondaryDependency,


    private val tertiary: TertiaryDependency,


    ) : CoroutineWorker(context, params) {


    override suspend fun doWork(): Result {


    val success = /* Complex codes to test */


    return if (success) {


    Result.success()


    } else {


    Result.failure()


    }


    }


    }
    Worker੄ ز੘җ ࠺ૉפझ ۽૒੉ ೠؘ ࢴৈ ੓णפ׮.

    View Slide

  65. @HiltWorker


    class ExampleWorker @AssistedInject constructor(


    @Assisted context: Context,


    @Assisted params: WorkerParameters,


    private val example: ExampleUseCase,


    ) : CoroutineWorker(context, params) {


    override suspend fun doWork(): Result {


    val success = example()


    return if (success) {


    Result.success()


    } else {


    Result.failure()


    }


    }


    }
    DIܳ ੉ਊೞৈ, ࠺ૉפझ ۽૒݅ ܻ࠙ೞৈ

    View Slide

  66. interface ExampleUseCase {


    operator suspend fun invoke(): Boolean


    }


    class ExampleUseCaseImpl @Inject constructor(


    private val primary: PrimaryDependency,


    private val secondary: SecondaryDependency,


    private val tertiary: TertiaryDependency,


    ) : ExampleUseCase {


    override operator suspend fun invoke(): Boolean {


    return /* Complex codes to test */


    }


    }


    @Module


    @InstallIn(SingletonComponent::class)


    interface TasksBindsModule {


    @Binds fun bindsExampleUseCase(impl: ExampleUseCaseImpl): ExampleUseCase


    }
    Worker੄ ৉ೡਸ ୭ࣗചೡ ࣻ ੓णפ׮.

    View Slide

  67. Section #2
    పझ౟ܳ Ҋ۰ೞৈ, CoroutineDispatcherܳ ૒੽ ੑ۱ೞ૑
    ঋҊ ੄ઓࢿ ઱ੑೞৈ ࢎਊೞח ߑߨਸ ࢓ಝࠄ׮.

    Qualifierܳ ੉ਊೞৈ, ׮নೠ CoroutineDispatcherܳ ҳ
    ࠙ೞৈ ઱ੑೡ ࣻ ੓׮.
    জীࢲ ࢎਊೞӝ ੸׼ೠ CoroutineScopeܳ ࢓ಝࠁ੗.

    ౠ൤, Application ژח Service ߧਤ ղীࢲ ௏ܖ౯ਸ ҙܻ
    ೞח ߑߨਸ ঌইࠁҊ, Hilt۽ ੄ઓࢿ ઱ੑೞח Ѫ੉ о੢ જ਷
    ߑߨੋ૑ Ҋ޹೧ ࠁ੗.
    CoroutineDispatcher CoroutineScope
    Thinking about Coroutines

    View Slide

  68. class Example {


    suspend fun doSomething(): Boolean {


    return withContext(Dispatchers.Default) { /* do something! */ }


    }


    }


    @Test


    fun test() {


    val example = Example()


    assertTrue(example.doSomething())


    }
    ௏ܖ౯੉ ನೣغݶ যڌѱ పझ౟ೞաਃ?
    Link: https://developer.android.com/kotlin/coroutines/coroutines-best-practices#inject-dispatchers

    View Slide

  69. class Example(


    private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default,


    ) {


    suspend fun doSomething(): Boolean {


    return withContext(defaultDispatcher) { /* do something! */ }


    }


    }


    @Test


    fun test() {


    val testDispatcher: TestDispatcher = UnconfinedTestDispatcher()


    val example = Example(testDispatcher)


    assertTrue(example.doSomething())


    }
    DIܳ ੉ਊೞৈ, TestDispatcherܳ ઱ੑೡ ࣻ ੓णפ׮.
    Link: https://developer.android.com/kotlin/coroutines/coroutines-best-practices#inject-dispatchers

    View Slide

  70. class Example @Inject constructor(


    private val defaultDispatcher: CoroutineDispatcher,


    ) {


    suspend fun doSomething(): Boolean {


    return withContext(defaultDispatcher) { /* do something! */ }


    }


    }


    @Module


    @InstallIn(SingletonComponent::class)


    object DispatchersModule {


    @Provides


    fun providesDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default


    }
    Hiltܳ ੉ਊೠ ௏٘ੑפ׮.

    View Slide

  71. class Example @Inject constructor(


    private val defaultDispatcher: CoroutineDispatcher,


    ) {


    suspend fun doSomething(): Boolean {


    return withContext(defaultDispatcher) { /* do something! */ }


    }


    }


    @Module


    @InstallIn(SingletonComponent::class)


    object DispatchersModule {


    @Provides


    fun providesDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default


    @Provides


    fun providesIoDispatcher(): CoroutineDispatcher = Dispatchers.IO // ???


    }
    Ӓؘ۠ ׮ܲ ਬഋ੉ ೙ਃೞ׮ݶ যڌѱ ೧ঠ ೡөਃ?

    View Slide

  72. @Qualifier


    @Retention(AnnotationRetention.BINARY)


    annotation class DefaultDispatcher


    @Qualifier


    @Retention(AnnotationRetention.BINARY)


    annotation class IoDispatcher


    @Module


    @InstallIn(SingletonComponent::class)


    object DispatchersModule {


    @DefaultDispatcher


    @Provides


    fun providesDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default


    @IoDispatcher


    @Provides


    fun providesIoDispatcher(): CoroutineDispatcher = Dispatchers.IO


    }
    Qualifierܳ ੉ਊೞݶ ؾפ׮.
    Link: https://medium.com/androiddevelopers/create-an-application-coroutinescope-using-hilt-dd444e721528

    View Slide

  73. class Example @Inject constructor(


    @DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher,


    ) {


    suspend fun doSomething(): Boolean {


    return withContext(defaultDispatcher) { /* do something! */ }


    }


    }


    @Module


    @InstallIn(SingletonComponent::class)


    object DispatchersModule {


    @DefaultDispatcher


    @Provides


    fun providesDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default


    @IoDispatcher


    @Provides


    fun providesIoDispatcher(): CoroutineDispatcher = Dispatchers.IO


    }
    Default Dispatcherܳ ઱ੑೞח ௏٘ੑפ׮.

    View Slide

  74. class Example @Inject constructor(


    @IoDispatcher private val ioDispatcher: CoroutineDispatcher,


    ) {


    suspend fun doSomething(): Boolean {


    return withContext(defaultDispatcher) { /* do something! */ }


    }


    }


    @Module


    @InstallIn(SingletonComponent::class)


    object DispatchersModule {


    @DefaultDispatcher


    @Provides


    fun providesDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default


    @IoDispatcher


    @Provides


    fun providesIoDispatcher(): CoroutineDispatcher = Dispatchers.IO


    }
    IO Dispatcherܳ ઱ੑೞח ௏٘ੑפ׮.

    View Slide

  75. Section #2
    Application ߧਤীࢲ CoroutineScopeܳ ࢎਊೞח ߑߨਸ ࢓ಝࠁ੗.

    ৘ܳ ٜয,

    • DataStore ୡӝച

    • ૑಴ ࣻ૘

    • ௿ۄ਋٘ ߔস
    CoroutineScope in Application?
    Link: https://medium.com/androiddevelopers/create-an-application-coroutinescope-using-hilt-dd444e721528

    View Slide

  76. class Example {


    fun doSomething() {


    GlobalScope.launch { // X


    /* do something! */


    }


    }


    }
    Link: https://medium.com/androiddevelopers/coroutines-patterns-for-work-that-shouldnt-be-cancelled-e26c40f142ad
    GlobalScopeח పझ౟ܳ য۵ѱ ٜ݅যࢲ ӂ੢ೞ૑ ঋणפ׮.

    View Slide

  77. class Example {


    fun doSomething() {


    // androidx.lifecycle:lifecycle-process


    ProcessLifecycleOwner.get().lifecycleScope.launch {


    /* do something! */


    }


    }


    }
    Link: https://medium.com/androiddevelopers/coroutines-patterns-for-work-that-shouldnt-be-cancelled-e26c40f142ad
    ؀উਵ۽ ProcessLifecycleOwner੉ ੓णפ׮݅,

    View Slide

  78. class Example @Inject constructor(


    @ApplicationScope private val lifecycleOwner: LifecycleOwner, // X


    ) {


    fun doSomething() {


    lifecycleOwner.lifecycleScope.launch {


    /* do something! */


    }


    }


    }


    @Module


    @InstallIn(SingletonComponent::class)


    object ApplicationModule {


    @ApplicationScope


    @Provides


    fun providesProcessLifecycleOwner(): LifecycleOwner = ProcessLifecycleOwner.get()


    }
    Link: https://developer.android.com/reference/androidx/lifecycle/ProcessLifecycleOwner
    ݃ଲо૑۽ పझ౟ܳ য۵ѱ ٜ݅য ӂ੢ೞ૑ ঋणפ׮.

    View Slide

  79. Library Internal
    // androidx.lifecycle:lifecycle-runtime-ktx


    public val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope


    get() = lifecycle.coroutineScope


    public val Lifecycle.coroutineScope: LifecycleCoroutineScope


    get() {


    while (true) {


    val existing = mInternalScopeRef.get() as LifecycleCoroutineScopeImpl?


    if (existing != null) {


    return existing


    }


    val newScope = LifecycleCoroutineScopeImpl(


    this,


    SupervisorJob() + Dispatchers.Main.immediate


    )


    if (mInternalScopeRef.compareAndSet(null, newScope)) {


    newScope.register()


    return newScope


    }




    Link: https://developer.android.com/topic/libraries/architecture/coroutines#lifecyclescope
    ؀न lifecycleScope ղࠗ ௏٘ܳ ଵҊೞৈ,

    View Slide

  80. @Module


    @InstallIn(SingletonComponent::class)


    object DispatchersModule {


    @ApplicationScope


    @Singleton


    @Provides


    fun providesApplicationCoroutineScope(


    @MainImmediateDispatcher mainImmediateDispatcher: CoroutineDispatcher


    ): CoroutineScope = CoroutineScope(context = SupervisorJob() + mainImmediateDispatcher)


    @MainImmediateDispatcher


    @Provides


    fun providesMainImmediateDispatcher(): CoroutineDispatcher = Dispatchers.Main.immediate


    }
    Application ߧਤ੄ CoroutineScopeܳ ૒੽ ࢤࢿೞח Ѫਸ ӂ੢೤פ׮.
    Link: https://medium.com/androiddevelopers/create-an-application-coroutinescope-using-hilt-dd444e721528

    View Slide

  81. @Module


    @InstallIn(SingletonComponent::class)


    object DispatchersModule {


    @ApplicationScope


    @Singleton


    @Provides


    fun providesApplicationCoroutineScope(


    @MainImmediateDispatcher mainImmediateDispatcher: CoroutineDispatcher


    ): CoroutineScope = CoroutineScope(context = SupervisorJob() + mainImmediateDispatcher)


    @MainImmediateDispatcher


    @Provides


    fun providesMainImmediateDispatcher(): CoroutineDispatcher = Dispatchers.Main.immediate


    }


    Dispatcherܳ ࢶఖೡ ٸ, Application ١ UI ۽૒ী ઱۽ ࢎਊೠ׮ݶ

    ࣽࢲ ࠁ੢੉ غח Main.immediateܳ ࢎਊೡ ࣻ ੓Ҋ,

    View Slide

  82. @Module


    @InstallIn(SingletonComponent::class)


    object DispatchersModule {


    @ApplicationScope


    @Singleton


    @Provides


    fun providesApplicationCoroutineScope(


    @DefaultDispatcher defaultDispatcher: CoroutineDispatcher


    ): CoroutineScope = CoroutineScope(context = SupervisorJob() + defaultDispatcher)


    @DefaultDispatcher


    @Provides


    fun providesDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default


    }


    Background ੘সী ઱۽ ࢎਊೠ׮ݶ

    Dispatcher ੹ജ੉ ੸ب۾ Defaultܳ ࢎਊೡ ࣻ ੓णפ׮.

    View Slide

  83. @Module


    @InstallIn(SingletonComponent::class)


    object DispatchersModule {


    @ApplicationScope


    @Singleton


    @Provides


    fun providesApplicationCoroutineScope(


    @DefaultDispatcher defaultDispatcher: CoroutineDispatcher


    ): CoroutineScope = CoroutineScope(context = SupervisorJob() + defaultDispatcher)


    }


    class Example @Inject constructor(


    @ApplicationScope private val applicationScope: CoroutineScope,


    ) {


    fun doSomething() {


    applicationScope.launch { /* do something! */ }


    }


    }
    Qualifier۽ CoroutineScopeܳ ઱ੑೞݶ ؾפ׮.

    View Slide

  84. Section #2
    Service ߧਤীࢲ CoroutineScopeܳ ࢎਊೞח ߑߨਸ ࢓ಝࠁ੗.

    ৘ܳ ٜয,

    • ׮਍۽٘/স۽٘

    • ਺ঈ ੤ࢤ

    • ղ࠺ѱ੉࣌
    CoroutineScope in Service?

    View Slide

  85. @Module


    @InstallIn(ServiceComponent::class)


    object ServiceModule {


    @ServiceScope // ???


    @ServiceScoped


    @Provides


    fun providesServiceScope(


    @MainImmediateDispatcher dispatcher: CoroutineDispatcher


    ): CoroutineScope = CoroutineScope(context = SupervisorJob() + dispatcher)


    }


    @Qualifier


    @Retention(AnnotationRetention.BINARY)


    annotation class ServiceScope
    ૒੽ ࢤࢿೞח ߑߨ੉ ݢ੷ ځয়ܰחؘਃ. Serviceо ઙܐؼ ٸח যڌѱ ஂࣗೡөਃ?

    View Slide

  86. class ExampleActivity : androidx.core.app.ComponentActivity() {


    private fun doSomething() {


    lifecycleScope.launch {


    /* do something! */


    }


    }


    }


    package androidx.core.app;


    public class ComponentActivity extends Activity implements LifecycleOwner { ... }
    ׮ܲ ஹನք౟৬ ࠺Ү೧ࠁݶ, Activityח Lifecycleਸ Ҋ۰ೠ CoroutineScopeܳ ઁҕೞҊ

    View Slide

  87. class ExampleFragment : androidx.fragment.app.Fragment() {


    private fun doSomething() {


    lifecycleScope.launch { /* do something! */ }


    viewLifecycleOwner.lifecycleScope.launch { /* do something! */ }


    }


    }


    package androidx.fragment.app;


    public class Fragment implements LifecycleOwner {


    @MainThread


    @NonNull


    public LifecycleOwner getViewLifecycleOwner() { ... }


    }
    Fragmentب Lifecycleਸ Ҋ۰ೠ CoroutineScopeܳ ઁҕ೤פ׮.

    View Slide

  88. class ExampleService : Service() {


    private fun doSomething() {


    lifecycleScope.launch { // X


    /* do something! */


    }


    }


    }


    package android.app;


    public abstract class Service extends ContextWrapper { ... } // X
    ইऔѱب Serviceח LifecycleOwnerܳ ҳഅೞ૑ ঋই, ࢎਊೡ ࣻ হחؘਃ.

    View Slide

  89. class ExampleService : LifecycleService() {


    private fun doSomething() {


    lifecycleScope.launch {


    /* do something! */


    }


    }


    }


    // androidx.lifecycle:lifecycle-service


    package androidx.lifecycle;


    public class LifecycleService extends Service implements LifecycleOwner { ... }
    Link: https://dagger.dev/hilt/entry-points
    ؀न LifecycleServiceܳ ੉ਊೞৈ, Lifecycleਸ Ҋ۰ೠ CoroutineScopeܳ ࢎਊೡ ࣻ ੓णפ׮.

    View Slide

  90. Section #2
    • Qualifierܳ ੉ਊೞৈ, ׮নೠ CoroutineDispatcherܳ ҳ࠙ೞৈ ઱ੑೡ ࣻ ੓׮.


    • Application ߧਤীࢲח ࢤݺ઱ӝо ೙ਃೠ ҃਋݅ ProcessLifecycleOwnerܳ ࢎਊೞҊ,

    Coroutines਷ DIܳ ੉ਊೞৈ Custom CoroutineScopeܳ ٜ݅য ࢎਊೞ੗.

    • উ٘۽੉٘ ೒ۖಬ੄ ҳࢿਃࣗীࢲ ഐ୹ೞח Coroutines਷

    Lifecycle-aware CoroutineScopeܳ ੉ਊೞ੗.

    • Activity, Fragment, Service(=LifecycleService) -> lifecycleScope

    • ViewModel -> viewModelScope
    Coroutines Summary
    Link: https://en.wikipedia.org/wiki/Dependency_injection

    View Slide

  91. Section #2
    • Hiltо ઁҕೞ૑ ঋח ҳࢿਃࣗীب EntryPointܳ ੉ਊೞৈ ੄ઓࢿਸ ઱ੑೡ ࣻ ੓׮.

    • Contextী ੽Ӕೡ ࣻ ੓׮ݶ Application ߧਤ੄ ੄ઓࢿਸ ઱ੑೡ ࣻ ੓׮.

    • ׮নೠ ۄ੉࠳۞ܻ৬ ো҅ೞৈ ഝਊೡ ࣻ ੓׮.

    • Startup, Glide ١١
    Thinking about EntryPoint
    Link: https://dagger.dev/hilt/entry-points

    View Slide

  92. // Startup: Constructor Injection (X)


    class ExampleInitializer @Inject constructor( // X


    private val bar: Bar,


    ) : Initializer {


    override fun create(context: Context): Example {


    ...


    }


    }
    Startupҗ э਷ ۄ੉࠳۞ܻܳ ੉ਊೞ׮ ࠁݶ, ࢤࢿ੗ ઱ੑਸ ೞ૑ ޅೡ ࣻ ੓णפ׮.

    View Slide

  93. // Startup: Field Injection (X)


    @AndroidEntryPoint // X


    class ExampleInitializer : Initializer {


    @Inject lateinit var bar: Bar,


    override fun create(context: Context): Example {


    ...


    }


    }
    ੉۠ ҃਋ীח AndroidEntryPointب ૑ਗೞ૑ ঋই ೙٘ ઱ੑب য۵ҳਃ.

    View Slide

  94. // Startup: Alternatives (?)


    class ExampleInitializer : Initializer {


    override fun create(context: Context): Example {


    ...


    }


    }
    ੉ ٸ, Contextী ੽Ӕೡ ࣻ ੓ਵݶ

    View Slide

  95. // Startup: EntryPoint


    class ExampleInitializer : Initializer {


    @EntryPoint


    @InstallIn(SingletonComponent::class)


    interface ExampleInitializerEntryPoint {


    fun providesBar(): Bar


    }


    override fun create(context: Context): Example {


    val entryPoint = EntryPointAccessors.fromApplication(


    context.applicationContext,


    ExampleInitializerEntryPoint::class.java


    )


    val bar = entryPoint.providesBar()


    ...


    }


    }
    EntryPointܳ ੉ਊೞৈ, Application ߧਤ੄ ੄ઓࢿਸ ઱ੑೡ ࣻ ੓णפ׮.

    View Slide

  96. // Glide: EntryPoint


    @GlideModule


    class ExampleAppGlideModule : AppGlideModule() {


    @EntryPoint


    @InstallIn(SingletonComponent::class)


    interface ExampleAppGlideModuleEntryPoint {


    fun providesBar(): Bar


    }




    override fun registerComponents(context: Context, glide: Glide, registry: Registry) {


    val entryPoint = EntryPointAccessors.fromApplication(


    context.applicationContext,


    ExampleAppGlideModuleEntryPoint::class.java


    )


    val bar = entryPoint.providesBar()


    ...


    }


    }
    ׮ܲ ৘द۽ Glide੄ ௏٘ੑפ׮.

    View Slide

  97. Section #2
    • Composeח ࢚కо ղ۰оҊ ੉߮౟о ৢۄоח

    ױߑೱ ؘ੉ఠ ൒ܴ(UDF) ಁఢਸ ӂ੢ೠ׮.

    • ݽٚ ੄ઓࢿਸ Composableী ݒѐ߸ࣻ۽ ੹׳ೡ

    ೙ਃח হب۾, CompositionLocalਸ ઁҕೠ׮.

    ੉۞ݶ ౠ੿ Composable ҅க ղীࢲ݅

    زੌೠ ੋझఢझܳ ࢎਊೡ ࣻ ੓׮.

    • Ӓؘ۠ জ ੹୓ীࢲ زੌೠ ੋझఢझܳ ࢎਊೞѢա

    ౠ੿ Composableীࢲ݅ ࢎਊೠ׮ݶ?
    Compose with EntryPoint
    Link: https://developer.android.com/jetpack/compose/state#state-hoisting

    View Slide

  98. // Option 1: State Hoisting


    Parents
    GrandParents Children GrandChildren
    Bar
    Bar Bar Bar
    Link: https://developer.android.com/jetpack/compose/state#state-hoisting
    ৘द۽ GrandChildrenীࢲ Barܳ ࢎਊೞ۰ݶ, пп੄ Composable ೣࣻܳ Ѣ୛ ੹׳ೡ ࣻ ੓णפ׮.

    View Slide

  99. // Option 1: State Hoisting


    @AndroidEntryPoint


    class ExampleActivity : Activity() {


    @Inject lateinit var bar: Bar


    override fun onCreate(savedInstanceState: Bundle?) {


    super.onCreate(savedInstanceState)


    setContent {


    GrandParents(bar = bar)


    }


    }


    }
    Link: https://developer.android.com/jetpack/compose/state#state-hoisting
    ௏٘۽ח, Activityী ઱ੑ߉਷ Barܳ GrandParentsী ੹׳ೞҊ

    View Slide

  100. // Option 1: State Hoisting


    @Composable fun GrandParents(bar: Bar) {


    Parents(bar = bar)


    }


    @Composable fun Parents(bar: Bar) {


    Children(bar = bar)


    }


    @Composable fun Children(bar: Bar) {


    GrandChildren(bar = bar)


    }


    @Composable fun GrandChildren(bar: Bar) {


    // Use bar


    }
    Link: https://developer.android.com/jetpack/compose/state#state-hoisting
    пп੄ Composableਸ Ѣ୛ GrandChildrenө૑ ੹׳ೡ ࣻ ੓णפ׮.

    View Slide

  101. // Option 1: State Hoisting


    @Composable fun GrandParents(bar: Bar) {


    Parents(bar = bar)


    }


    @Composable fun Parents(bar: Bar) {


    Children(bar = bar)


    }


    @Composable fun Children(bar: Bar) {


    GrandChildren(bar = bar)


    }


    @Composable fun GrandChildren(bar: Bar) {


    // Use bar


    }
    Link: https://developer.android.com/jetpack/compose/state#state-hoisting
    Ӓؘ۠ GrandChildrenীࢲ݅ ࢎਊೞחؘ, ݽٚ Composableਸ Ѣ୛оח Ѫ੉ ઑӘ җ೧ࠁ੉૑ ঋաਃ?

    View Slide

  102. // Option 2: CompositionLocal


    Parents
    GrandParents Children GrandChildren
    Bar
    Link: https://developer.android.com/jetpack/compose/compositionlocal
    LocalBar Bar
    CompositionLocalਸ ੉ਊೞৈ ѐࢶೡ ࣻ ੓णפ׮.

    View Slide

  103. // Option 2: CompositionLocal


    @AndroidEntryPoint


    class ExampleActivity : Activity() {


    @Inject lateinit var bar: Bar


    override fun onCreate(savedInstanceState: Bundle?) {


    super.onCreate(savedInstanceState)


    setContent {


    CompositionLocalProvider(LocalBar provides bar) {


    GrandParents()


    }


    }


    }


    }


    val LocalBar = staticCompositionLocalOf { error("...") }
    Link: https://developer.android.com/jetpack/compose/compositionlocal
    ௏٘۽ח, Activityী ઱ੑ ߉਷ Barܳ LocalBar۽ ઁҕೞҊ

    View Slide

  104. // Option 2: CompositionLocal


    @Composable fun GrandParents() {


    Parents()


    }


    @Composable fun Parents() {


    Children()


    }


    @Composable fun Children() {


    GrandChildren()


    }


    @Composable fun GrandChildren() {


    val bar: Bar = LocalBar.current


    // Use bar


    }
    Link: https://developer.android.com/jetpack/compose/compositionlocal
    GrandChildrenীࢲ LocalBarܳ ా೧, Barܳ оઉоݶ ؾפ׮.

    View Slide

  105. // Option 2: CompositionLocal


    @AndroidEntryPoint


    class ExampleActivity : Activity() {


    @Inject lateinit var bar: Bar


    @Inject lateinit var foo: Foo


    override fun onCreate(savedInstanceState: Bundle?) {


    super.onCreate(savedInstanceState)


    setContent {


    CompositionLocalProvider(


    LocalBar provides bar,


    LocalFoo provides foo,


    ) { ... }


    }


    }


    }
    Link: https://developer.android.com/jetpack/compose/compositionlocal#alternatives
    Ӓؘ۠ CompositionLocalܳ ੄بೠ؀۽ ࢎਊೠ Ѫ੉ ݏਸөਃ?

    ӒܻҊ ઱ੑ೧ঠ ೞח ೦ݾ੉ ੼੼ טযդ׮ݶ ҡଳਸөਃ?

    View Slide

  106. // Option 3: EntryPoint


    Parents
    GrandParents Children GrandChildren
    Bar
    Link: https://dagger.dev/hilt/entry-points
    য૵ݶ EntryPointо ೞա੄ ؀উ੉ ؼ ࣻب ੓ਸ Ѫ эणפ׮.

    View Slide

  107. // Option 3:


    @AndroidEntryPoint


    class ExampleActivity : Activity() {


    // @Inject lateinit var bar: Bar


    override fun onCreate(savedInstanceState: Bundle?) {


    super.onCreate(savedInstanceState)


    setContent {


    GrandParents()


    }


    }


    }
    Link: https://dagger.dev/hilt/entry-points
    ௏٘۽ח, ؊ ੉࢚ Activityী ઱ੑ߉ਸ ೙ਃо হणפ׮.

    View Slide

  108. // Option 3:


    @Composable fun GrandParents() {


    Parents()


    }


    @Composable fun Parents() {


    Children()


    }


    @Composable fun Children() {


    GrandChildren()


    }


    @Composable fun GrandChildren() {


    val bar: Bar = rememberBar()


    // Use bar


    }
    Link: https://dagger.dev/hilt/entry-points
    CompositionLocal୊ۢ GrandChildrenীࢲ ߄۽ ଵઑೡ ࣻ ੓णפ׮.

    View Slide

  109. // Option 3:


    @Composable private fun rememberBar(): Bar {


    val context = LocalContext.current


    return remember {


    val entryPoint = EntryPointAccessors.fromApplication(


    context.applicationContext,


    ExampleEntryPoint::class.java,


    )


    entryPoint.providesBar()


    }


    }


    @EntryPoint


    @InstallIn(SingletonComponent::class)


    interface ExampleEntryPoint {


    fun providesBar(): Bar


    }
    Link: https://dagger.dev/hilt/entry-points
    ߑߨ਷ LocalContextܳ ੉ਊ೧,

    Application ߧਤ੄ ੄ઓࢿਸ ઱ੑೞח Ѫੑפ׮.

    View Slide

  110. DIܳ ࢎਊೞ۰ח ݾ੸ী ٮۄ, Dagger Hilt ࢎਊߑߨਸ Ҋ޹೧ࠁ੗.

    • ୭؀ೠ ೒ۖಬ ੄ઓࢿਸ ઴੉੗. (ex. WorkManager)

    • উ٘۽੉٘ ҳࢿਃࣗীࢲח Lifecycle-aware Coroutinesਸ ࢎਊೞ੗.

    • EntryPointܳ ੉ਊೞৈ, ۄ੉࠳۞ܻ ҙ۲ ௏٘ীب ੄ઓࢿ ઱ੑ੉ оמೞ׮.
    Wrap-Up

    View Slide

  111. Sungyong An NAVER WEBTOON
    Android GDE / ?
    Thank You
    Sungyong An NAVER WEBTOON
    Android GDE / @fornewid
    Slide Link: https://speakerdeck.com/fornewid

    View Slide