简体   繁体   English

使用Dagger 2在库模块中注入应用程序上下文

[英]Injecting application context in library module with Dagger 2

I'm building an app with some features: a ContentProvider a SyncAdapter, a Job service and related persistence logic. 我正在构建一个具有一些功能的应用程序:ContentProvider,SyncAdapter,作业服务和相关的持久性逻辑。 On top on these there are the Activities with the UI. 除此之外,还有UI的活动。 I'm trying to put all said features in a separate library module, because in theory their logic is stand-alone and would be reusable by any application. 我试图将所有所述功能放在一个单独的库模块中,因为从理论上讲它们的逻辑是独立的,可以被任何应用程序重用。

Now comes Dagger2. 现在来了Dagger2。 The first node (main Component) of my library's dependency graph does need to provide Context, and this Context has to be injected from the Application, because the library scope has the same lifecycle of the application. 我的库的依赖图的第一个节点(主要组件)确实需要提供Context,并且必须从Application注入此Context,因为库范围与应用程序具有相同的生命周期。 To be self contained, obviously, my library should not directly use my Application class. 显然,要自包含,我的库不应该直接使用我的Application类。

These are the possibilities I thought of: 这些是我想到的可能性:

  • Build the library's main Component in my Application and store it in a global static class/enum as suggested here but I'm concerned that using such a static reference could be an anti-pattern. 在我的应用程序中构建库的主要组件并将其存储在这里建议的全局静态类/枚举中,但我担心使用这样的静态引用可能是反模式。
  • Pack in the library an Application class which builds the library scoped Component, cast app context to this class in the library to use the component and then extend this Application class on the main app. 在库中打包一个Application类,它构建库范围的Component,将应用程序上下文转换为库中的此类以使用该组件,然后在主应用程序上扩展此Application类。 This works, but if there's more than one library it's not viable anymore. 这样可行,但如果有多个库则不再可行。
  • Use the factory pattern: put provision methods in the library component that provide the factory which in turn is given the locally available context as a parameter. 使用工厂模式:在库组件中提供提供方法,提供工厂,然后将工厂提供本地可用的上下文作为参数。 (As explained here ). (至于解释这里 )。 This seems viable, although it adds extra complexity. 这似乎是可行的,但它增加了额外的复杂性。
  • Last but non the least, give up trying to modularize the components, since being dependent on the application context breaks the concept of modularity. 最后但并非最不重要的是,放弃尝试模块化组件,因为依赖于应用程序上下文打破了模块化的概念。

What is the correct way to do this? 这样做的正确方法是什么?

Dagger 2 for Android comes to the rescue. 适用于Android的Dagger 2来救援。 It provides the concept of AndroidInjector , which is a Component that can be used to inject an instance in a static way, without having to know the dependency provider. 它提供了AndroidInjector的概念,它是一个可用于以静态方式注入实例的Component ,而无需知道依赖提供程序。 Moreover, using the Dagger- prefixed classes provided out of the box, the injected dependencies look like coming from nowhere. 此外,使用Dagger-用的Dagger-前缀类,注入的依赖项看起来像是从哪儿冒出来的。 Awesome. 真棒。

All you have to do is declare in the library a top-level Module which is installed in the Application Component . 您所要做的就是在库中声明一个安装在Application Component的顶级Module This Module will provide all the dependencies and the SubComponent s needed by the library, which will automatically inherit the @AppContext Context that you seeded in the dependency graph, ready to be injected anywhere in you library, as well as every dependency you provide through the main Application Component . Module将提供库所需的所有依赖项和SubComponent ,它将自动继承您在依赖关系图中播种的@AppContext Context ,随时可以在您的库中的任何位置注入,以及您通过主要应用Component

Here's a short example (written in Kotlin): 这是一个简短的例子(用Kotlin编写):

@Component(modules = [
    AndroidSupportInjectionModule::class,
    AppModule::class,
    LibraryModule::class //plug-in the library to the dependency graph
])
@Singleton
interface AppComponent : AndroidInjector<App> {

    @Component.Builder
    abstract class Builder : AndroidInjector.Builder<App>() {

        @BindsInstance
        abstract fun appContext(@AppContext context: Context)

        override fun seedInstance(instance: App) {
            appContext(instance)
        }
    }
}

Edit: extended examples 编辑:扩展示例

An example of the Application subclass: Application子类的一个示例:

// DaggerApplication provides out-of-the-box support to all the AndroidInjectors.
// See the class' code to understand the magic.
public class App extends DaggerApplication {

@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
    // We only provide its own Injector, the Application Injector,
    // that is the previous AppComponent
    return DaggerAppComponent.builder().create(this);
}

And in your Android library: 在您的Android库中:

@Module
public abstract class LibraryModule {

    @ContributesAndroidInjector
    public abstract LibraryActivity contributeLibraryActivityInjector();

}

public class LibraryActivity extends DaggerAppCompatActivity {

    @Inject
    @AppContext
    Context appContext;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceSate);
        // here you automagically have your injected application context!
        ExternalSingleton.getInstance(appContext)
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM