Computer >> 컴퓨터 >  >> 체계 >> Android

Dagger 2를 사용하여 앱에서 종속성 주입을 구현하는 방법

Kriptofolio 앱 시리즈 - 4부

종속성 주입은 코드를 크게 향상시킵니다. 코드를 보다 모듈화되고 유연하며 테스트 가능하게 만듭니다. 사실 그 이름은 그 뒤에 숨은 아이디어보다 더 복잡하게 들립니다.

시리즈의 이 부분에서 우리는 의존성 주입에 대해 배울 것입니다. 그런 다음 "Kriptofolio"(이전의 "My Crypto Coins") 앱에서 구현합니다. Dagger 2를 사용할 것입니다. Dagger 2는 Android에서 가장 널리 사용되는 오픈 소스 종속성 주입 프레임워크입니다. 이는 학습 곡선이 충분히 어렵다고 생각하더라도 최신 앱을 만드는 데 필요한 귀중한 기술입니다.

시리즈 콘텐츠

  • 소개:2018–2019년 최신 Android 앱을 구축하기 위한 로드맵
  • 1부:SOLID 원칙 소개
  • 2부:Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기
  • 3부:아키텍처에 관한 모든 것:다양한 아키텍처 패턴 탐색 및 앱에서 이를 사용하는 방법
  • 4부:Dagger 2를 사용하여 앱에서 종속성 주입을 구현하는 방법(현재 위치)
  • 5부:Retrofit, OkHttp, Gson, Glide 및 Coroutine을 사용하여 RESTful 웹 서비스 처리

종속성 주입이란 무엇입니까?

의존성 주입을 설명하려면 먼저 프로그래밍에서 의존성이 무엇을 의미하는지 이해해야 합니다. 종속성은 객체 중 하나가 다른 객체의 구체적인 구현에 의존하는 경우입니다. 다른 개체 내에서 개체를 인스턴스화할 때마다 코드에서 종속성을 식별할 수 있습니다. 실용적인 예를 살펴보겠습니다.

class MyAppClass() {

    private val library: MyLibrary = MyLibrary(true)
    ...
}

class MyLibrary(private val useSpecialFeature: Boolean) {
    
    ...
}

이 예제에서 볼 수 있듯이 클래스 MyAppClass 라이브러리 클래스 MyLibrary의 구체적인 구성 및 구현에 직접적으로 의존합니다. . 언젠가 타사 라이브러리를 대신 사용하고 싶다면 어떻게 하시겠습니까? 정확히 동일한 라이브러리 구성을 사용하려는 다른 클래스를 갖고 싶다면 어떻게 해야 합니까? 코드를 검색해야 할 때마다 정확한 위치를 찾아 변경해야 합니다. 이는 몇 가지 예일 뿐입니다.

아이디어는 응용 프로그램 구성 요소 간의 긴밀한 결합이 프로젝트가 성장함에 따라 개발 작업을 더 어렵게 만들 것이라는 것입니다. 문제를 피하기 위해 설명된 연결을 풀기 위해 종속성 주입을 사용합시다.

class MyAppClass(private val library: MyLibrary) {
    
    ...
}

class MyLibrary(private val useSpecialFeature: Boolean) {
    
    ...
}

그게 다야, 그것은 매우 원시적인 의존성 주입의 예이다. 새 MyLibrary을 만들고 구성하는 대신 MyAppClass 클래스 내부의 클래스 객체 , 생성자에 전달하거나 주입하기만 하면 됩니다. 그래서 MyAppClass MyLibrary에 대해 완전히 무책임할 수 있습니다. .

Dagger 2가 무엇인가요?

Dagger는 Java 및 Android 모두를 위한 완전히 정적 컴파일 타임 오픈 소스 종속성 주입 프레임워크입니다. 이 기사에서는 Google이 유지 관리하는 두 번째 버전에 대해 이야기하겠습니다. Square는 이전 버전을 만들었습니다.

Dagger 2는 현재까지 구축된 가장 효율적인 종속성 주입 프레임워크 중 하나로 간주됩니다. 실제로 Dagger 1, Dagger 2 및 Dagger 2.10을 비교하면 각 구현이 다르다는 것을 알 수 있습니다. 저자가 수행한 중요한 변경 사항이 있으므로 매번 다시 학습해야 합니다. 이 기사를 작성할 때 나는 Dagger 2.16 버전을 사용하고 있으며 우리는 그것에만 집중할 것입니다.

이제 종속성 주입에 대해 이해했듯이 클래스는 종속성을 생성하거나 포함해서는 안 됩니다. 대신 그들은 외부에서 모든 것을 얻어야 합니다. 따라서 Dagger 2를 사용할 때 이 프레임워크는 필요한 모든 종속성을 제공합니다.

이것은 우리를 위해 많은 상용구 코드를 생성함으로써 이루어집니다. 생성된 코드는 완전히 추적 가능하며 사용자가 직접 작성할 수 있는 코드를 모방합니다. Dagger 2는 Java로 작성되었으며 주석 처리기에 의해 생성된 코드도 Java 코드가 됩니다.

그러나 문제나 수정 없이 Kotlin과 함께 작동합니다. Kotlin은 Java와 완전히 상호 운용 가능합니다. 유사한 프레임워크와 비교하면 Dagger 2는 덜 동적입니다. 리플렉션이 있는 런타임이 아닌 컴파일 시간에 작동합니다. 반사 사용이 전혀 없습니다. 즉, 이 프레임워크를 설정하고 학습하기가 더 어려워질 것입니다. 컴파일 시간 안전성으로 성능 향상을 제공합니다.

도구 없이 수동 종속성 주입

이전 부분의 My Crypto Coins 앱 소스 코드에서 종속성 주입 도구를 사용하지 않고 개체를 주입하는 코드가 있음을 눈치채셨을 것입니다. 잘 작동하며 이 솔루션은 이와 같은 작은 앱에 충분할 것입니다. 유틸리티 패키지 살펴보기:

/**
 * Static methods used to inject classes needed for various Activities and Fragments.
 */
object InjectorUtils {

    private fun getCryptocurrencyRepository(context: Context): CryptocurrencyRepository {
        return CryptocurrencyRepository.getInstance(
                AppDatabase.getInstance(context).cryptocurrencyDao())
    }

    fun provideMainViewModelFactory(
            application: Application
    ): MainViewModelFactory {
        val repository = getCryptocurrencyRepository(application)
        return MainViewModelFactory(application, repository)
    }

    fun provideAddSearchViewModelFactory(
            context: Context
    ): AddSearchViewModelFactory {
        val repository = getCryptocurrencyRepository(context)
        return AddSearchViewModelFactory(repository)
    }
}

보시다시피 이 클래스는 모든 작업을 수행합니다. 액티비티 또는 이를 필요로 하는 프래그먼트에 대한 ViewModel 팩토리를 생성합니다.

/**
 * Factory for creating a [MainViewModel] with a constructor that takes a
 * [CryptocurrencyRepository].
 */
class MainViewModelFactory(private val application: Application, private val repository: CryptocurrencyRepository) : ViewModelProvider.NewInstanceFactory() {

    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return MainViewModel(application, repository) as T
    }

}

그런 다음 InjectorUtils를 사용합니다. 특정 ViewModel 팩토리를 가져와야 하는 곳:

/**
 * A placeholder fragment containing a simple view.
 */
class MainListFragment : Fragment() {

    ...

    private lateinit var viewModel: MainViewModel

    ...

    override fun onActivityCreated(savedInstanceState: Bundle?) {

        super.onActivityCreated(savedInstanceState)

        setupList()
        ...
    }

    ...

    private fun subscribeUi(activity: FragmentActivity) {

        // This is the old way how we were injecting code before using Dagger.
        val factory = InjectorUtils.provideMainViewModelFactory(activity.application)

        // Obtain ViewModel from ViewModelProviders, using parent activity as LifecycleOwner.
        viewModel = ViewModelProviders.of(activity, factory).get(MainViewModel::class.java)

        ...
    }

}

MainListFragment에서 볼 수 있듯이 클래스는 CryptocurrencyRepository도 모릅니다. 또는 AppDatabase . InjectorUtils 클래스에서 성공적으로 구성된 팩토리를 가져옵니다. 실제로 이것은 하나의 간단한 방법입니다. 우리는 그것을 없애고 고급 의존성 주입을 위해 Dagger 2 도구를 설정하는 방법을 배울 것입니다. 이 앱이 기능과 코드 면에서 확장된다면 수동 솔루션보다 전문적인 종속성 주입 프레임워크를 사용하는 이점을 정말 빨리 볼 수 있을 것이라고 믿어 의심치 않습니다.

InjectorUtils을 삭제해 보겠습니다. 지금 바로 수업을 듣고 My Crypto Coins 앱 소스 코드에서 Dagger 2를 설정하는 방법을 배우십시오.

Kotlin을 사용한 MVVM의 종속성 주입

ViewModel, 활동 및 조각으로 Dagger 2를 설정하는 방법

이제 My Crypto Coins 앱 프로젝트에서 Dagger 2를 단계별로 설정해 보겠습니다.

시작하려면 Kotlin의 고유한 주석 처리 도구(kapt)를 활성화해야 합니다. 그런 다음 특별한 Dagger 2 종속성을 추가합니다.

다음 행을 gradle 파일에 추가하여 이 작업을 수행할 수 있습니다.

apply plugin: 'kotlin-kapt' // For annotation processing

...

implementation "com.google.dagger:dagger:$versions.dagger"
implementation "com.google.dagger:dagger-android:$versions.dagger"
implementation "com.google.dagger:dagger-android-support:$versions.dagger"
kapt "com.google.dagger:dagger-compiler:$versions.dagger"
kapt "com.google.dagger:dagger-android-processor:$versions.dagger"
모듈:앱

Kapt 플러그인을 사용하면 컴파일러가 Java와 Kotlin 간의 상호 운용성에 필요한 스텁 클래스를 생성할 수 있습니다. 편의를 위해 모든 종속성을 사용하여 별도의 gradle 파일에 구체적인 Dagger 2 버전을 정의합니다.

def versions = [:]

versions.dagger = "2.16"

ext.versions = versions

사용 가능한 최신 버전을 찾으려면 Github의 Dagger 2 공식 리포지토리에서 릴리스를 확인하세요.

이제 애플리케이션 App을 만듭니다. 수업.

이 클래스 세트가 이미 있는 경우 이 단계를 건너뜁니다. 작업을 마치면 잠시 그대로 두지만 나중에 다시 올 것입니다.

class App : Application() {

    override fun onCreate() {
        super.onCreate()
    }

}

My Crypto Coins 앱의 경우 이전에 애플리케이션 클래스를 이미 생성했습니다.

다음으로 매니페스트 파일을 업데이트하여 App 수업.

이전에 이미 수행한 경우 이 작업을 건너뜁니다.

<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.baruckis.mycryptocoins">

    <application
        android:name=".App"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        ...

My Crypto Coins 앱의 경우 이미 App도 설정했습니다. 이전에 매니페스트에 클래스를 추가했습니다.

이제 dependencyinjection이라는 새 패키지를 만들어 보겠습니다. .

여기에 Dagger 구현과 관련된 모든 파일을 보관할 것입니다.

AppModule 생성 애플리케이션 전체에 종속성을 제공할 클래스 모듈입니다.

/**
 * AppModule will provide app-wide dependencies for a part of the application.
 * It should initialize objects used across our application, such as Room database, Retrofit, Shared Preference, etc.
 */
@Module(includes = [ViewModelsModule::class])
class AppModule() {

    @Singleton // Annotation informs Dagger compiler that the instance should be created only once in the entire lifecycle of the application.
    @Provides // Annotation informs Dagger compiler that this method is the constructor for the Context return type.
    fun provideContext(app: App): Context = app // Using provide as a prefix is a common convention but not a requirement.

    @Singleton
    @Provides
    fun provideCryptocurrencyRepository(context: Context): CryptocurrencyRepository {
        return CryptocurrencyRepository.getInstance(AppDatabase.getInstance(context).cryptocurrencyDao())
    }
}

보시다시피 Dagger 모듈을 생성하려면 특수 @Module로 주석을 달아야 합니다. 주석. 프로젝트에는 일반적으로 여러 Dagger 모듈이 있습니다. 그 중 하나가 앱 전체에 종속성을 제공하는 것이 일반적입니다. 이 AppModule Room 데이터베이스, Retrofit, Shared Preference 등과 같이 응용 프로그램 전체에서 사용되는 개체를 초기화하는 데 사용됩니다.

예를 들어, AppModule이 앱의 어디에서나 액세스해야 하는 경우에 대비하여 Context 개체를 제공하는 매우 일반적인 시나리오에 대해 논의할 수 있습니다. 코드를 분석하여 그 방법을 알아보겠습니다.

특별한 Dagger 주석 @Provides을 사용해야 합니다. . 메서드가 특정 유형의 종속성(이 경우에는 Context 개체)을 제공한다는 것을 Dagger에 알려줍니다. 따라서 앱의 어딘가에서 컨텍스트를 주입하도록 요청할 때 AppModule은 Dagger가 컨텍스트를 찾는 곳입니다. Dagger는 반환 유형에만 관심이 있으므로 메서드 이름은 중요하지 않습니다. 제공 접두사로 메서드 이름을 지정하는 것이 일반적이지만 원하는 대로 지정할 수 있습니다.

@Singleton 동일한 메소드에 적용된 주석은 Dagger 주석의 일부가 아닙니다. javax 패키지 안에 포함되어 있습니다. 이 주석은 Dagger에 해당 종속성의 단일 인스턴스만 있어야 함을 알려줍니다.

객체의 다른 인스턴스가 이미 사용 가능한지 확인하기 위해 상용구 코드를 작성할 필요가 없습니다. 코드를 생성할 때 Dagger는 이 주석으로 인해 모든 논리를 처리합니다. AppModule에는 다른 모듈인 ViewModelsModule이 포함되어 있습니다. 지금 만들어 봅시다.

ViewModelsModule 생성 클래스 모듈. 이 모듈은 애플리케이션 전체에 ViewModel을 제공하는 역할을 합니다.

/**
 * Will be responsible for providing ViewModels.
 */
@Module
abstract class ViewModelsModule {

    // We'd like to take this implementation of the ViewModel class and make it available in an injectable map with MainViewModel::class as a key to that map.
    @Binds
    @IntoMap
    @ViewModelKey(MainViewModel::class) // We use a restriction on multibound map defined with @ViewModelKey annotation, and if don't need any, we should use @ClassKey annotation provided by Dagger.
    abstract fun bindMainViewModel(mainViewModel: MainViewModel): ViewModel

    @Binds
    @IntoMap
    @ViewModelKey(AddSearchViewModel::class)
    abstract fun bindAddSearchViewModel(addSearchViewModel: AddSearchViewModel): ViewModel

    @Binds
    abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
}

이 모듈은 Dagger 2 기능 맵 다중 바인딩을 사용합니다. 그것을 사용하여 우리는 앱의 어느 곳에서나 주입할 수 있는 지도에 선택한 객체를 제공합니다. Dagger 주석 조합 사용 @Binds , @IntoMap 사용자 지정 주석 @ViewModelKey (이것은 우리가 만들 것입니다), MainViewModel::class 키를 사용하여 지도 내부에 항목을 만듭니다. 및 값 MainViewModel 사례. 우리는 몇 가지 일반적인 ViewModelFactory의 도움으로 특정 팩토리를 바인딩합니다. 수업. 이 클래스를 만들어야 합니다.

맞춤 주석 클래스 만들기 ViewModelKey .

/**
 * An annotation class which tells dagger that it can be used to determine keys in multi bound maps.
 */
@MustBeDocumented
@Target(
        AnnotationTarget.FUNCTION,
        AnnotationTarget.PROPERTY_GETTER,
        AnnotationTarget.PROPERTY_SETTER
)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>) // We might use only those classes which inherit from ViewModel.

이 클래스는 ViewModelsModule에서 ViewModel을 바인딩하는 데 사용됩니다. . 특정 주석 @ViewModelKey 지도의 키를 나타냅니다. 키는 ViewModel에서 상속되는 클래스만 될 수 있습니다. .

ViewModelFactory 생성 수업.

/**
 * Factory to auto-generate a Class to Provider Map.
 * We use Provider<T> to create an injectable object at a later time.
 */
@Suppress("UNCHECKED_CAST")
@Singleton
class ViewModelFactory @Inject constructor(private val viewModelsMap: Map<Class<out ViewModel>,
        @JvmSuppressWildcards Provider<ViewModel>>) : ViewModelProvider.Factory {

    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        var creator: Provider<out ViewModel>? = viewModelsMap[modelClass]
        if (creator == null) {
            for (entry in viewModelsMap.entries) {
                if (modelClass.isAssignableFrom(entry.key)) {
                    creator = entry.value
                    break
                }
            }
        }
        if (creator == null) {
            throw IllegalArgumentException("Unknown model class $modelClass")
        }

        try {
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}

ViewModelFactory ViewModel을 동적으로 생성하는 데 도움이 되는 유틸리티 클래스입니다. 여기에서 생성된 맵을 인수로 제공합니다. create() 메소드는 지도에서 올바른 인스턴스를 선택할 수 있습니다.

ActivityBuildersModule 생성 클래스 모듈.

/**
 * All activities intended to use Dagger @Inject should be listed here.
 */
@Module
abstract class ActivityBuildersModule {

    @ContributesAndroidInjector(modules = [MainListFragmetBuildersModule::class]) // Where to apply the injection.
    abstract fun contributeMainActivity(): MainActivity

    @ContributesAndroidInjector
    abstract fun contributeAddSearchActivity(): AddSearchActivity
}

이 모듈은 모든 활동을 구성하는 역할을 합니다. AndroidInjector을 생성합니다. 이 클래스에 정의된 모든 활동에 대해 그런 다음 AndroidInjection.inject(this)을 사용하여 활동에 개체를 삽입할 수 있습니다. onCreate에서 활동 수명 주기의 기능. 이 모듈은 프래그먼트를 담당하는 또 다른 별도의 모듈도 사용합니다. 이 모듈은 다음에 만들 것입니다.

MainListFragmetBuildersModule 생성 클래스 모듈.

/**
 * All fragments related to MainActivity intended to use Dagger @Inject should be listed here.
 */
@Module
abstract class MainListFragmetBuildersModule {

    @ContributesAndroidInjector() // Attaches fragment to Dagger graph.
    abstract fun contributeMainListFragment(): MainListFragment
}

이 모듈은 MainActivity와 관련된 모든 조각을 빌드합니다. . AndroidInjector을 생성합니다. 이 클래스에 정의된 모든 프래그먼트에 대해 AndroidSupportInjection.inject(this)을 사용하여 개체를 프래그먼트에 주입할 수 있습니다. onAttach 프래그먼트 수명 주기의 함수입니다.

AppComponent 생성 클래스 구성 요소.

/**
 * Singleton component interface for the app. It ties all the modules together.
 * The component is used to connect objects to their dependencies.
 * Dagger will auto-generate DaggerAppComponent which is used for initialization at Application.
 */
@Singleton
@Component(
        modules = [
            // AndroidSupportInjectionModule is a class of Dagger and we don't need to create it.
            // If you want to use injection in fragment then you should use AndroidSupportInjectionModule.class else use AndroidInjectionModule.
            AndroidSupportInjectionModule::class,
            AppModule::class,
            ActivityBuildersModule::class
        ]
)
interface AppComponent {

    @Component.Builder // Used for instantiation of a component.
    interface Builder {

        @BindsInstance // Bind our application instance to our Dagger graph.
        fun application(application: App): Builder

        fun build(): AppComponent
    }

    // The application which is allowed to request the dependencies declared by the modules
    // (by means of the @Inject annotation) should be declared here with individual inject() methods.
    fun inject(app: App)
}

컴포넌트는 매우 중요한 클래스입니다. 위의 모든 것이 함께 작업을 시작할 수 있습니다. 개체를 종속성에 연결하여 이를 수행합니다. Dagger는 이 인터페이스를 사용하여 종속성 주입을 수행하는 데 필요한 코드를 생성합니다.

구성 요소 클래스를 만들려면 Dagger 주석 @Component을 사용해야 합니다. . 모듈 목록을 입력으로 사용합니다. 다른 주석 @Component.Builder 일부 인스턴스를 구성 요소에 바인딩할 수 있습니다.

그런 다음 그래프 개체를 생성합니다.

이 순간에 모든 모듈과 구성 요소 설정이 완료되었습니다. Android Studio IDE 내에서 Build -> Make Module을 선택하여 그래프 개체를 생성할 수 있습니다. 향후 단계를 위해 이 세대가 필요합니다.

이제 Injectable 생성 인터페이스.

/**
 * It is just a plain empty marker interface, which tells to automatically inject activities or fragments if they implement it.
 */
interface Injectable

이는 향후 단계에도 필요합니다. Injectable 인터페이스는 자동으로 주입할 수 있는 액티비티나 프래그먼트에 의해 구현되어야 합니다.

AppInjector라는 새 도우미 클래스 만들기 .

/**
 * It is simple helper class to avoid calling inject method on each activity or fragment.
 */
object AppInjector {
    fun init(app: App) {
        // Here we initialize Dagger. DaggerAppComponent is auto-generated from AppComponent.
        DaggerAppComponent.builder().application(app).build().inject(app)

        app.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {
            override fun onActivityPaused(activity: Activity) {

            }

            override fun onActivityResumed(activity: Activity) {

            }

            override fun onActivityStarted(activity: Activity) {

            }

            override fun onActivityDestroyed(activity: Activity) {

            }

            override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle?) {

            }

            override fun onActivityStopped(activity: Activity) {

            }

            override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
                handleActivity(activity)
            }
        })
    }

    private fun handleActivity(activity: Activity) {
        if (activity is HasSupportFragmentInjector || activity is Injectable) {
            // Calling inject() method will cause Dagger to locate the singletons in the dependency graph to try to find a matching return type.
            // If it finds one, it assigns the references to the respective fields.
            AndroidInjection.inject(activity)
        }

        if (activity is FragmentActivity) {
            activity.supportFragmentManager.registerFragmentLifecycleCallbacks(object : FragmentManager.FragmentLifecycleCallbacks() {
                override fun onFragmentCreated(fragmentManager: FragmentManager, fragment: Fragment, savedInstanceState: Bundle?) {
                    if (fragment is Injectable) {
                        AndroidSupportInjection.inject(fragment)
                    }
                }
            }, true)
        }
    }

}

각 액티비티 또는 프래그먼트에서 주입 메소드 호출을 피하기 위한 단순한 도우미 클래스입니다.

다음으로 App을 설정합니다. 이전에 이미 만든 클래스입니다.

class App : Application(), HasActivityInjector {

    @Inject // It implements Dagger machinery of finding appropriate injector factory for a type.
    lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>

    override fun onCreate() {
        super.onCreate()

        // Initialize in order to automatically inject activities and fragments if they implement Injectable interface.
        AppInjector.init(this)

        ...
    }


    // This is required by HasActivityInjector interface to setup Dagger for Activity.
    override fun activityInjector(): AndroidInjector<Activity> = dispatchingAndroidInjector
}

애플리케이션에 액티비티가 있기 때문에 HasActivityInjector을 구현해야 합니다. 상호 작용. DaggerAppComponent에서 Android Studio에서 호출한 오류가 표시되는 경우 , 이전 단계에서 지적한 대로 새 파일을 생성하지 않았기 때문입니다.

그래서 MainActivity을 설정하세요. 기본 ViewModel 팩토리를 주입하고 프래그먼트 주입 지원을 추가합니다.

// To support injecting fragments which belongs to this activity we need to implement HasSupportFragmentInjector.
// We would not need to implement it, if our activity did not contain any fragments or the fragments did not need to inject anything.
class MainActivity : AppCompatActivity(), HasSupportFragmentInjector {

    @Inject
    lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>

    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory
    private lateinit var mainViewModel: MainViewModel


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Obtain ViewModel from ViewModelProviders, using this activity as LifecycleOwner.
        mainViewModel = ViewModelProviders.of(this, viewModelFactory).get(MainViewModel::class.java)

        ...
    }

    ...

    override fun supportFragmentInjector(): AndroidInjector<Fragment> = dispatchingAndroidInjector

    ...
}

액티비티에 자식 프래그먼트가 있기 때문에 HasSupportFragmentInjector을 구현해야 합니다. 상호 작용. 조각에 주입할 계획이기 때문에 이것도 필요합니다. 우리의 활동은 그것이 주입되는 방법에 대해 알지 않아야 합니다. AndroidInjection.inject(this)를 사용합니다. 재정의 onCreate() 내부의 코드 라인 방법.

inject() 호출 메서드를 사용하면 Dagger 2가 종속성 그래프에서 싱글톤을 찾아 일치하는 반환 유형을 찾으려고 합니다. 그러나 이전에 생성된 AppInjector에 의해 수행되기 때문에 여기에 코드를 작성할 필요가 없습니다. 애플리케이션 클래스 내에서 초기화한 도우미 클래스입니다.

그런 다음 MainListFragment을 설정합니다. 기본 ViewModel 팩토리를 삽입합니다.

/**
 * A placeholder fragment containing a simple view.
 */
class MainListFragment : Fragment(), Injectable {

    ...

    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory
    private lateinit var viewModel: MainViewModel

    ...

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        ...
        subscribeUi(activity!!)
    }

    ...

    private fun subscribeUi(activity: FragmentActivity) {

        // Obtain ViewModel from ViewModelProviders, using parent activity as LifecycleOwner.
        viewModel = ViewModelProviders.of(activity, viewModelFactory).get(MainViewModel::class.java)
        
        ...

    }

}

액티비티와 유사하게 프래그먼트를 주입 가능하게 하려면 onAttach 코드를 작성해야 하는 메소드 AndroidSupportInjection.inject(this) . 그러나 이것은 AppInjector에 의해 수행된 작업입니다. 도우미이므로 건너뛸 수 있습니다. Injectable를 추가해야 합니다. 도우미가 작동하도록 이전에 만든 인터페이스입니다.

축하합니다. My Crypto Coins 앱 프로젝트에서 Dagger 2를 구현했습니다. 물론 이 문서는 앱에 Dagger 2를 즉시 배포하기 위한 빠른 가이드이지만 이에 대한 자세한 내용은 아닙니다. 기본에 대해 길을 잃는다면 이 주제를 계속 연구하는 것이 좋습니다.

저장소

GitHub에서 업데이트된 "Kriptofolio"(이전의 "My Crypto Coins") 앱의 소스 코드를 확인하세요.

GitHub에서 소스 보기

아츄! 읽어 주셔서 감사합니다! 저는 원래 이 게시물을 2018년 10월 7일에 제 개인 블로그 www.baruckis.com에 게시했습니다.