dagger2 注入_如何使用Dagger 2在您的应用程序中实现依赖注入

dagger2 注入

Kriptofolio应用程序系列-第4部分 (Kriptofolio app series - Part 4)

Dependency injection will significantly improve your code. It makes your code more modular, flexible and testable. Actually its name sounds more complicated than the idea which stands behind it.

依赖注入将大大改善您的代码。 它使您的代码更具模块化,灵活性和可测试性。 实际上,它的名字听起来比背后的想法更复杂。

In this part of the series we are going to learn about dependency injection. We will then implement it in “Kriptofolio” (previously “My Crypto Coins”) app. We are going to use Dagger 2. Dagger 2 is the most popular open-source dependency injection framework for Android. This is a valuable skill to have for creating modern apps, even thought the learning curve is hard enough.

在本系列的这一部分中,我们将学习依赖注入。 然后,我们将在“ Kriptofolio”(以前称为“我的加密货币”)应用中实施该应用。 我们将使用Dagger2。Dagger 2是Android上最受欢迎的开源依赖注入框架。 这是创建现代应用程序所具有的一项宝贵技能,即使认为学习难度足够大也是如此。

系列内容 (Series content)

什么是依赖注入? (What is Dependency Injection?)

To explain dependency injection first we have to understand what dependency means in programming. A dependency is when one of the objects depends on the concrete implementation of another object. You can identify a dependency in your code whenever you instantiate an object within another. Let’s take a look at a practical example.

首先要解释依赖注入,我们必须了解依赖在编程中的含义。 依赖关系是指其中一个对象依赖于另一个对象的具体实现。 每当在另一个对象中实例化一个对象时,就可以在代码中标识一个依赖关系。 让我们看一个实际的例子。

class MyAppClass() {

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

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

As you see from this example your class MyAppClass will depend directly on concrete configuration and implementation of your library class MyLibrary. What if you would like one day to use a third-party library instead? What if you would like to have another class where you would like to use exactly the same library configuration? Every time you will have to search through your code, find exact place and change it. It’s just a few examples.

从该示例中可以看到,类MyAppClass将直接取决于库类MyLibrary具体配置和实现。 如果您希望有一天改用第三方库怎么办? 如果您想在另一个类中使用完全相同的库配置怎么办? 每次您都必须搜索代码,找到确切的位置并进行更改。 这只是几个例子。

The idea is that this tight coupling between the components of the application will make your development work harder as your project grows. To avoid any problems, let’s use dependency injection for loosening the coupling described.

这个想法是,应用程序组件之间的紧密耦合将使您的开发随着项目的增长而更加艰苦。 为了避免任何问题,让我们使用依赖注入来松开所描述的耦合。

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

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

That’s it, that’s a very primitive dependency injection example. Instead of creating and configuring a new MyLibrary class object inside your class MyAppClass, you just pass or inject it into the constructor. So MyAppClass can be totally irresponsible for MyLibrary.

就是这样,这是一个非常原始的依赖项注入示例。 无需在类MyAppClass中创建和配置新的MyLibrary类对象,只需将其传递或注入到构造函数中即可。 因此MyAppClassMyLibrary可能完全不负责任。

什么是匕首2? (What is Dagger 2?)

Dagger is a fully static, compile-time, open-source dependency injection framework for both Java and Android. In this article I will be talking about its second version which Google maintains. Square created its earlier version.

Dagger是适用于Java和Android的全静态,编译时,开源依赖注入框架。 在本文中,我将讨论Google维护的第二版。 Square创建了其早期版本。

Dagger 2 is considered to be one of the most efficient dependency injection frameworks built to date. Actually if you compare Dagger 1, Dagger 2 and Dagger 2.10 you would discover each implementation is different. You need to relearn it each time as there were significant changes done by the authors. When writing this article I am using Dagger 2.16 version and we are going to focus only on it.

Dagger 2被认为是迄今为止构建的最有效的依赖注入框架之一。 实际上,如果比较Dagger 1,Dagger 2和Dagger 2.10,您会发现每种实现都是不同的。 由于作者进行了重大更改,因此您每次都需要重新学习它。 在撰写本文时,我使用的是Dagger 2.16版本,我们将仅关注它。

As you now understand about dependency injection, our classes should not create or have dependencies. Instead they need to get everything from outside. So when using Dagger 2, this framework will provide all the dependencies needed.

正如您现在对依赖项注入的了解一样,我们的类不应创建或具有依赖项。 相反,他们需要从外部获得一切。 因此,当使用Dagger 2时,此框架将提供所需的所有依赖项。

It does this by generating a lot of boilerplate code for us. That generated code will be fully traceable and will mimic the code which a user may write by hand. Dagger 2 is written in Java and the code generated by its annotation processor will be Java code too.

它通过为我们生成大量样板代码来完成此任务。 生成的代码将是完全可追踪的,并且将模仿用户可能用手编写的代码。 Dagger 2用Java编写,其注释处理器生成的代码也将是Java代码。

However it works with Kotlin without any problems or modifications. Remember that Kotlin is fully interoperable with Java. If compared to similar frameworks, Dagger 2 is a less dynamic one. It works at compile time rather than at run-time with reflection. There is no reflection usage at all. All that means is that this framework will be harder to set up and to learn. It will provide performance boost with compile-time safety.

但是,它可以与Kotlin一起使用,没有任何问题或修改。 请记住,Kotlin可与Java完全互操作。 如果将Dagger 2与类似框架进行比较,则动态性较差。 它在编译时起作用,而不是在运行时使用反射。 根本没有反射用法。 这意味着该框架将更难以建立和学习。 它将通过编译时安全性提高性能。

无需工具的手动依赖注入 (Manual Dependency Injection without tools)

You may have noticed in the My Crypto Coins app source code from the previous part that there is a piece of code for injecting objects without using any dependency injection tools. It works fine, and this solution would be good enough for such a small app like this. Take a look at the utilities package:

您可能已经在上一部分的“我的加密货币”应用程序源代码中注意到,有一段代码无需使用任何依赖项注入工具即可注入对象。 它工作正常,对于像这样的小型应用程序,此解决方案就足够了。 看一下实用程序包:

/**
 * 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)
    }
}

As you see this class will do all the work. It will create ViewModel factories for activities or fragments that require them.

如您所见,该课程将完成所有工作。 它将为需要它们的活动或片段创建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
    }

}

Then you use InjectorUtils class like this where you need to get a specific ViewModel factory:

然后,您需要使用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)

        ...
    }

}

As you see our MainListFragment class don’t even know about CryptocurrencyRepository or AppDatabase. It gets a successfully constructed factory from InjectorUtils class. Actually this is one simple way to do it. We are going to get rid of it and learn how to setup Dagger 2 tool for advanced dependency injection. If this app would expand in functionality and code, I don’t doubt we would start seeing benefits really fast of using a professional dependency injection framework over a manual solution.

如您所见,我们的MainListFragment类甚至不了解CryptocurrencyRepositoryAppDatabase 。 它从InjectorUtils类获得成功建造的工厂。 实际上,这是一种简单的方法。 我们将摆脱它,并学习如何设置Dagger 2工具来进行高级依赖注入。 如果此应用程序的功能和代码能够扩展,我毫不怀疑我们将开始真正看到使用专业的依赖项注入框架而不是手动解决方案的好处。

So let’s delete InjectorUtils class right now and learn how to setup Dagger 2 in My Crypto Coins app source code.

因此,让我们现在删除InjectorUtils类,并学习如何在My Crypto Coins应用程序源代码中设置Dagger 2。

Kotlin对MVVM的依赖注入 (Dependency Injection for MVVM with Kotlin)

如何使用ViewModels,Activity和Fragments设置Dagger 2 (How to setup Dagger 2 with ViewModels, Activities and Fragments)

Now we’ll go through the Dagger 2 step by step setup on the My Crypto Coins app project.

现在,我们将在My Crypto Coins应用程序项目中逐步完成Dagger 2的设置。

To begin, you should enable Kotlin’s own Annotation Processing Tool (kapt). Then add special Dagger 2 dependencies.

首先,您应该启用Kotlin自己的注释处理工具 (kapt)。 然后添加特殊的Dagger 2依赖项。

You can do this by adding these lines to your gradle file:

您可以通过将以下行添加到gradle文件中来实现此目的:

Kapt plugin will enable the compiler to generate stub classes required for interoperability between Java and Kotlin. For convenience we will define the concrete Dagger 2 version in a separate gradle file, as we do that with all our dependencies.

Kapt插件将使编译器能够生成Java和Kotlin之间互操作性所需的桩类。 为方便起见,我们将在单独的gradle文件中定义具体的Dagger 2版本,因为我们对所有依赖项都进行了定义。

def versions = [:]

versions.dagger = "2.16"

ext.versions = versions

To find the latest version available check the releases at Dagger 2's official repository on Github.

要查找可用的最新版本,请在GithubDagger 2的官方存储库中查看发行版。

Now, create your application App class.

现在,创建您的应用程序App类。

Skip this if you already have this class set. After you’ve done that, we will leave it as it is for a while, but come back later.

如果您已经设置了此类,请跳过此步骤。 完成此操作后,我们将其保留一段时间,但稍后再返回。

class App : Application() {

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

}

For My Crypto Coins app, we already have created the application class earlier.

对于My Crypto Coins应用程序,我们已经在前面创建了应用程序类。

Next, update your manifest file to enable your App class.

接下来,更新清单文件以启用您的App类。

Skip this if you have already done that before.

如果您之前已经做过,请跳过此步骤。

<manifest xmlns:android="http://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"
        ...

For My Crypto Coins app, we’ve also already set the App class in the manifest earlier.

对于“我的加密货币”应用程序,我们还早已在清单中设置了App类。

Now let’s create a new package called dependencyinjection.

现在,让我们创建一个名为dependencyinjection的新程序包。

Here we are going to keep all the files related to Dagger implementation.

在这里,我们将保留与Dagger实现有关的所有文件。

Create AppModule class module which will provide dependencies all over your application.

创建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())
    }
}

As you see, to create a Dagger module we need to annotate it with the special @Module annotation. Projects usually have multiple Dagger modules. It is typical for one of them to provide app-wide dependencies. This AppModule will be used to initialize objects used across our application, such as Room database, Retrofit, Shared Preference, etc.

如您所见,要创建Dagger模块,我们需要使用特殊的@Module注释@Module注释。 项目通常具有多个Dagger模块。 其中之一通常提供应用程序范围的依赖关系。 此AppModule将用于初始化在我们的应用程序中使用的对象,例如Room数据库,Retrofit,Shared Preference等。

As an example, we could discuss a very common scenario for AppModule to provide a Context object in case we need it to get access to it anywhere in our app. Let’s analyze the code to see how to do that.

例如,我们可以讨论一个非常常见的方案,让AppModule提供Context对象,以防我们需要在应用程序中的任何地方访问它。 让我们分析代码,看看如何做到这一点。

We need to use a special Dagger annotation @Provides. It tells Dagger that the method provides a specific type of dependency, in our case, a Context object. So when somewhere in the app we request to inject a Context, AppModule is the place where Dagger finds it. And it does not matter the names of our methods, as Dagger cares only about the return type. It is only common practice to name the method with provide prefix, but it can be anything you want.

我们需要使用特殊的Dagger注释@Provides 。 它告诉Dagger,该方法提供了特定类型的依赖关系,在我们的例子中是Context对象。 因此,当我们请求在应用程序中的某个位置注入上下文时,AppModule是Dagger找到它的地方。 而且我们的方法名称无关紧要,因为Dagger只关心返回类型。 通常使用提供前缀来命名方法,但这可以是您想要的任何东西。

The @Singleton annotation which you see applied to the same method is not part of the Dagger annotations. It is contained inside the javax package. This annotation tells Dagger that there should only be a single instance of that dependency.

您看到应用于同一方法的@Singleton批注不是Dagger批注的一部分。 它包含在javax包中。 该注释告诉Dagger,该依赖项应该只有一个实例。

You don’t need to write the boilerplate code to check if another instance of the object is already available. When generating the code Dagger will handle all that logic for you because of this annotation. Notice that our AppModule includes another module ViewModelsModule. Let’s create it now.

您无需编写样板代码来检查对象的另一个实例是否已经可用。 生成代码时,由于此注释,Dagger将为您处理所有逻辑。 注意,我们的AppModule包含另一个模块ViewModelsModule。 现在创建它。

Create a ViewModelsModule class module. This module will be responsible for providing ViewModels all over your application.

创建一个ViewModelsModule类模块。 该模块将负责在整个应用程序中提供ViewModels。

/**
 * 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
}

This module uses Dagger 2 feature map multi bindings. Using it, we contribute objects of our choosing into a map that becomes injectable anywhere in our app. Using the combination of Dagger annotations @Binds, @IntoMap and our custom annotation @ViewModelKey(this one we are going to create), we create an entry inside our map with key MainViewModel::class and value MainViewModel instance. We bind specific factory with the help of some common ViewModelFactory class. We need to create this class.

该模块使用Dagger 2功能图多重绑定。 使用它,我们将选择的对象贡献给地图,该地图可在应用程序中的任何位置注入。 使用Dagger注释@Binds@IntoMap和我们的自定义注释@ViewModelKey (我们将要创建的注释)的组合,我们在地图内部使用键MainViewModel::class和值MainViewModel实例创建一个条目。 我们借助一些常见的ViewModelFactory类绑定特定的工厂。 我们需要创建此类。

Create a custom annotation class ViewModelKey.

创建一个自定义注释类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.

This class is used for binding ViewModels in the ViewModelsModule. The specific annotation @ViewModelKey represents the key of our map. Our key can be only a class that inherits from ViewModel.

此类用于在ViewModelsModule中绑定ViewModelsModule 。 特定的注释@ViewModelKey表示地图的键。 我们的密钥只能是从ViewModel继承的类。

Create the ViewModelFactory class.

创建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)
        }
    }
}

This ViewModelFactory is a utility class which helps you dynamically create ViewModels. Here you provide the generated map as an argument. The create() method will be able to pick the right instance from the map.

ViewModelFactory是一个实用程序类,可帮助您动态创建ViewModels。 在这里,您将生成的地图作为参数提供。 create()方法将能够从地图中选择合适的实例。

Create the ActivityBuildersModule class module.

创建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
}

This module is responsible for constructing all your activities. It will generate AndroidInjector for all Activities defined in this class. Then objects can be injected into activities using AndroidInjection.inject(this) in the onCreate function from the activity lifecycle. Notice that this module also uses another separate module responsible for fragments. We will create this module next.

该模块负责构建您的所有活动。 它将为此类中定义的所有Activity生成AndroidInjector 。 然后,可以从活动生命周期使用onCreate函数中的AndroidInjection.inject(this)将对象注入活动中。 请注意,该模块还使用另一个单独的模块负责片段。 接下来,我们将创建此模块。

Create the MainListFragmetBuildersModule class module.

创建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
}

This module will build all your fragments related to MainActivity. It will generate AndroidInjector for all Fragments defined in this class. Objects can be injected into Fragments using AndroidSupportInjection.inject(this) in the onAttach function from the fragment lifecycle.

该模块将构建与MainActivity相关的所有片段。 它将为此类中定义的所有片段生成AndroidInjector 。 从片段生命周期开始,可以使用onAttach函数中的AndroidSupportInjection.inject(this)将对象注入到片段中。

Create the AppComponent class component.

创建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)
}

Component is a very important class. It will enable all the above to start working together. It does this by connecting objects to their dependencies. Dagger will use this interface to generate the code necessary to perform the dependency injection.

组件是非常重要的一类。 这将使以上所有内容可以一起开始工作。 它通过将对象连接到它们的依赖项来实现。 Dagger将使用此接口来生成执行依赖项注入所需的代码。

To create a component class you will need to use Dagger annotation @Component. It takes a list of modules as an input. Another annotation @Component.Builder allows us to bind some instance to component.

要创建组件类,您将需要使用Dagger注解@Component 。 它以模块列表作为输入。 另一个注释@Component.Builder允许我们将某些实例绑定到component。

Then generate a graph object.

然后生成一个图形对象。

At this moment you have all your modules and your component setup. You can generate your graph object by selecting Build -> Make Module inside your Android Studio IDE. We will need this generation for future steps.

此时,您已经拥有所有模块和组件设置。 您可以通过在Android Studio IDE中选择Build-> Make Module来生成图形对象。 我们将需要这一代人来进行后续步骤。

Now create an Injectable interface.

现在创建一个Injectable接口。

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

This will also be needed for future steps. Injectable interface should be implemented by activities or fragments which we want to be injectable automatically.

这对于以后的步骤也将是必需的。 Injectable接口应由我们希望自动Injectable的活动或片段实现。

Create a new helper class named AppInjector.

创建一个名为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)
        }
    }

}

It is just a simple helper class to avoid calling the inject method on each activity or fragment.

这只是避免在每个活动或片段上调用inject方法的简单帮助程序类。

Next, setup the App class which we already created before.

接下来,设置我们之前已经创建的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
}

Because the application has activities, we need to implement the HasActivityInjector interface. If you see an error called out by Android Studio on DaggerAppComponent, it is because you have not generated a new file, as was pointed out in the previous step.

由于应用程序具有活动,因此我们需要实现HasActivityInjector接口。 如果您在DaggerAppComponent上看到Android Studio发出的错误,是因为您没有生成新文件,如上一步中所述。

So, setup MainActivity to inject the main ViewModel factory and add a support for fragment injections.

因此,设置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

    ...
}

Because our activities have child fragments we need to implement HasSupportFragmentInjector interface. We also need this because we plan to make injections into our fragments. Our activity should not know about how it is injected. We use the AndroidInjection.inject(this) code line inside overriding onCreate() method.

因为我们的活动有子片段,所以我们需要实现HasSupportFragmentInjector接口。 我们还需要这样做,因为我们计划对片段进行注射。 我们的活动不应该知道如何注射。 我们在覆盖onCreate()方法中使用了AndroidInjection.inject(this)代码行。

Calling inject() method will cause Dagger 2 to locate the singletons in the dependency graph to try to find a matching return type. However we don’t need to write any code here because it’s done for us by previously created AppInjector helper class which we initialized inside our application class.

调用inject()方法将使Dagger 2在依赖关系图中定位单例,以尝试查找匹配的返回类型。 但是,我们无需在此处编写任何代码,因为它是由先前创建的AppInjector帮助器类为我们完成的,该类在我们的应用程序类内部进行了初始化。

Then, setup MainListFragment to inject the main ViewModel factory.

然后,设置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)
        
        ...

    }

}

Similar to activities, if we want our fragment to be injectable, then into its onAttach method we should write the code AndroidSupportInjection.inject(this). But again this is a job done by the AppInjector helper, so we can skip that. Just notice that we need to add the Injectable interface which we created earlier for the helper to work.

与活动类似,如果我们希望片段是可注入的, onAttach在其onAttach方法中编写代码AndroidSupportInjection.inject(this) 。 但这又是AppInjector帮助程序完成的工作,因此我们可以跳过该工作。 只需注意,我们需要添加我们先前创建的Injectable接口,以使助手工作。

Congratulations, we’ve implemented Dagger 2 in the My Crypto Coins app project. Of course this article is a quick guide to deploy Dagger 2 in your app straight away, but not deep coverage of it. I recommend that you continue researching this topic if you feel lost on the basics.

恭喜,我们已在My Crypto Coins应用程序项目中实现了Dagger 2。 当然,本文是快速在您的应用程序中部署Dagger 2的快速指南,但没有深入介绍它。 如果您不了解基本知识,建议您继续研究此主题。

资料库 (Repository)

Check out the source code of the updated “Kriptofolio” (previously “My Crypto Coins”) app on GitHub.

在GitHub上查看更新的“ Kriptofolio”(以前称为“我的加密货币”)应用程序的源代码。

在GitHub上查看源代码 (View Source On GitHub)


Ačiū! Thanks for reading! I originally published this post for my personal blog www.baruckis.com on October 7, 2018.

阿奇! 谢谢阅读! 我最初于2018年10月7日在我的个人博客www.baruckis.com上发布了这篇文章。

翻译自: https://www.freecodecamp.org/news/kriptofolio-app-series-part-4/

dagger2 注入

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值