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

dev*_*cca 6 android dependency-injection android-library dagger-2

我正在构建一个具有一些功能的应用程序:ContentProvider,SyncAdapter,作业服务和相关的持久性逻辑.除此之外,还有UI的活动.我试图将所有所述功能放在一个单独的库模块中,因为从理论上讲它们的逻辑是独立的,可以被任何应用程序重用.

现在来了Dagger2.我的库的依赖图的第一个节点(主要组件)确实需要提供Context,并且必须从Application注入此Context,因为库范围与应用程序具有相同的生命周期.显然,要自包含,我的库不应该直接使用我的Application类.

这些是我想到的可能性:

  • 在我的应用程序中构建库的主要组件并将其存储在这里建议的全局静态类/枚举中,但我担心使用这样的静态引用可能是反模式.
  • 在库中打包一个Application类,它构建库范围的Component,将应用程序上下文转换为库中的此类以使用该组件,然后在主应用程序上扩展此Application类.这样可行,但如果有多个库则不再可行.
  • 使用工厂模式:在库组件中提供提供方法,提供工厂,然后将工厂提供本地可用的上下文作为参数.(至于解释这里).这似乎是可行的,但它增加了额外的复杂性.
  • 最后但并非最不重要的是,放弃尝试模块化组件,因为依赖于应用程序上下文打破了模块化的概念.

这样做的正确方法是什么?

dev*_*cca 8

适用于Android的Dagger 2来救援.它提供的概念AndroidInjector,这是一种Component可以用来以静态方式注入一个实例,而不必知道依赖供应商.此外,使用Dagger-开箱即用的前缀类,注入的依赖项看起来像是从哪儿冒出来的.真棒.

您所要做的就是在库中声明一个Module安装在Application中的顶级Component.这Module将提供SubComponent库所需的所有依赖项和s,它将自动继承@AppContext Context您在依赖关系图中播种的内容,随时可以在库中的任何位置注入,以及通过主Application提供的每个依赖项Component.

这是一个简短的例子(用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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:扩展示例

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);
}
Run Code Online (Sandbox Code Playgroud)

在您的Android库中:

@Module
public abstract class LibraryModule {

    @ContributesAndroidInjector
    public abstract LibraryActivity contributeLibraryActivityInjector();

}
Run Code Online (Sandbox Code Playgroud)
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)
    }
}
Run Code Online (Sandbox Code Playgroud)