如何在不强制消费应用程序使用 Dagger 的情况下构建基于 Dagger 的 Android 库?

How can I build a Dagger-based Android library without forcing consuming applications to use Dagger?

我正在开发一个 Android 库,它基本上是我编写的一些 REST 服务的客户端。我有多个存储 classes、网络队列、解析器等,并且像许多这样的 classes 一样,它们依赖于 ContextSharedPreferences 之类的东西由 Context 构成。这些对象都隐藏在外观 class 后面,因此我图书馆的消费者看不到它们或直接与它们交互。

为了我自己的理智,我想使用 Dagger 2 进行依赖注入,以在我的库中内部管理这些 classes 的实例。但是,我不想强​​制使用我的库的应用程序自己使用 Dagger;仅仅因为我选择使用 Dagger 并不意味着我的用户必须这样做。

我看到的所有教程似乎都期望我正在构建一个应用程序,而不仅仅是一个库。其中许多教程告诉我应该让我的 Application class 继承自 DaggerApplication。不过,就我而言,我的图书馆中根本没有 Application(或任何 ActivityService classes),我也不想我的用户必须使用 Dagger 的基础 classes.

那么,如果 "leaking" 库中没有 Dagger,我该如何使用它呢?我找到了部分答案 here,但我不确定如何调整作者的 "wrapper" 模式来处理我对 Context 的依赖。我可以只将上下文传递到包装器的 getComponent() 方法中,还是 Dagger 能够通过其他方式获取上下文引用?

一个库几乎就像一个应用程序(当涉及到 Dagger 时)。是的,您没有 application 对象,但您实际上并不需要。

作为您图书馆的消费者,我希望它易于使用,所以我根本不想知道 dagger 是什么(或者如果您在内部使用它)。

让您的用户在第一次调用您的图书馆时传递 Context(例如)。有一个 DaggerInjector (我认为你的示例称之为包装器),它有一个对你的 Component 接口的静态引用。

示例(因此,只是一个通用示例):

public class DaggerInjector {

    private static YourComponent component;

    private DaggerInjector() {
        super();
    }

    public static YourComponent getComponent() {
        return component;
    }

    public static YourComponent buildComponent(Context context) {
        component = DaggerYourComponent
                .builder()
                .yourModule(new YourModule(context))
                .build();
        return component;
    }
}

你的“模块”看起来像:

@Module
public class YourModule {

    private Context context;

    public YourModule(Context context) {
        this.context = context;
    }

    @Provides
    @Singleton
    final Context providesContext() {
        return context;
    }
}

使用方法:

让你的用户调用一个方法(或者如果组件为空,你自己第一次调用它):

DaggerInjector.buildComponent(context);

这将确保初始化 Dagger 组件并生成代码。了解调用 buildComponent 是一项昂贵的任务(Dagger 必须做很多事情!)所以只调用一次(除非您需要使用仅在运行时已知的不同值重新初始化库)。

有些库只是在每次调用时询问上下文,所以这不是不可能的;然后,您可以在第一次调用时初始化匕首(通过检查注入器中的 getComponent() 是否为 null)。

在你的 DaggerInjector.getComponent() 不再为 null 之后,你现在可以添加 @Inject 和适当的 "injectable" 东西......

例如:在 YourModule 你可以:

@Provides
SomeObject providesSomeObject() {
    return new SomeObject();
}

// THIS “Context” here is automatically injected by Dagger thanks to the above.
@Provides
@Singleton
SomeOtherObject providesSomeOtherObject(Context context) {
    return new SomeOtherObject(context); //assume this one needs it
}

并且在任何 "injectable" 对象中(即,在您的组件中具有 inject 方法的对象……)您可以:

public class AnObjectThatWantsToInjectStuff {

    @Inject
    SomeObject someObject;
    @Inject 
    SomeOtherObject someOtherObject;

    public AnObjectThatWantsToInjectStuff() {
          super();
          DaggerInjector.getComponent().inject(this);

          // you can now use someObject and someOtherObject
    }
}

要使上述工作正常,您需要在 YourComponent(这是一个接口)中编写如下代码:

void inject(AnObjectThatWantsToInjectStuff object);

(否则编译时调用DaggerInjector.getComponent().inject(this)会失败)

请注意,我从未将上下文传递给 YourInjectableContext,Dagger 已经知道如何获取它。

小心泄漏。对于 all/most 情况,我建议您存储 context.getApplicationContext() 而不是简单的 Context (除非您明确需要 Activity 上下文来膨胀 layouts/theme 目的,应用程序上下文消费应用程序提供的就是您所需要的)。