【问题标题】:How can I build a Dagger-based Android library without forcing consuming applications to use Dagger?如何在不强制消费应用程序使用 Dagger 的情况下构建基于 Dagger 的 Android 库?
【发布时间】:2017-07-10 22:37:31
【问题描述】:

我正在开发一个 Android 库,它基本上是我编写的一些 REST 服务的客户端。我有几个存储类、网络队列、解析器等等,和许多这样的类一样,它们依赖于Context 或从Context 构造的SharedPreferences 之类的东西。这些对象都隐藏在外观类后面,因此我的库的使用者看不到它们或直接与它们交互。

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

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

那么我怎样才能使用 Dagger 而不会“泄漏”出我的图书馆呢?我找到了部分答案here,但我不确定如何调整作者的“包装器”模式来处理我对Context 的依赖。我可以将上下文传递给包装器的 getComponent() 方法,还是 Dagger 能够以其他方式获取上下文引用?

【问题讨论】:

    标签: java android dagger-2 dagger


    【解决方案1】:

    几乎类似于应用程序(当涉及到 Dagger 时)。是的,您没有application 对象,但您并不需要它。

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

    让您的用户在第一次调用您的库时传递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 必须做很多事情!)所以只做一次(除非您需要使用仅在运行时已知的不同值重新初始化库)。

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

    在您的DaggerInjector.getComponent() 不再为空之后,您现在可以添加@Inject 和适当的“可注入”的东西......

    例如:在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
    }
    

    并且在任何“可注入”对象(即,在您的组件中具有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 已经知道如何获取它。

    小心泄漏。我建议您在所有/大多数情况下存储 context.getApplicationContext() 而不是简单的 Context(除非您明确需要 Activity 上下文来扩展布局/主题,否则您只需要使用应用程序提供的应用程序上下文)。

    【讨论】:

    • 非常感谢您的回答!我已听从您的建议,一切正常。
    • 有人用吗?遵循相同的过程但无法正常工作。我在构建应用程序时收到“找不到符号类 DaggerYourComponent”错误。 @Dalbergia,它是否在没有 android 的 UI 组件的情况下工作?
    • 它确实对我有用,显然 OP 也使它起作用。 @TejasMehta 尝试清理/重建或其他东西。您的错误意味着您的组件未正确生成或您使用的名称不正确。
    • 感谢@MartinMarconcini 在多次清理/重建后解决了这个问题。它现在可以工作了。
    • @TejasMehta,是的,MartinMarconcini 的回答是正确的,正如您所发现的。 “DaggerYourComponent”是“YourComponent”接口的一个实现,由 Dagger 作为构建过程的一部分生成,所以你必须在类被识别之前强制构建。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-04
    • 2017-10-06
    • 1970-01-01
    • 1970-01-01
    • 2015-08-21
    相关资源
    最近更新 更多