当前位置: 首页 > 知识库问答 >
问题:

如何将缺失的注入从模块(complete=false)从匕首1迁移到匕首2

庄嘉
2023-03-14

我有一个库项目/模块,Android应用程序和普通java应用程序都使用它。在Dagger 1中,此项目/模块具有属性complete=false。其中有一个@Inject字段,任何类实现或@Provides方法都不能满足该字段。其想法是强制具有complete=true的“顶部”模块提供特定于系统的实现

举个例子:在library项目中,我有一个ActLogin活动,它有字段@Inject@Named(“app version”)mAppVersion。此字段的值在登录到服务器时使用。ActLogin由使用此库的多个应用程序使用。每个应用程序的模块都具有complete=true,并使用@provides@Named(“应用程序版本”)provideAppVersion()提供值

Dagger 2迁移文档(http://google.github.io/dagger/dagger-1-migration.html)国家:

Dagger 2模块全部声明为complete=false和library=true

并在同一时间的“主要”文件页(http://google.github.io/dagger/)国家:

Dagger注释处理器是严格的,如果任何绑定无效或不完整,将导致编译器错误。

后者显然是正确的,因为当尝试使用未满足的注入进行构建时,会产生错误(error:java.lang.String不能在没有@Provides-或@products注释方法的情况下提供)。

问题是:是否有可能将这种方法(推迟提供注入)迁移到Dagger2,以及如何迁移?

另外,最初我认为在库的@Module中提供一些虚拟值是一个肮脏的解决办法,但再次强调,Dagger 2中不能有模块重写(这是一种WTF(!!!)。模块重写是我创建单元测试时最有用的功能)。可能我遗漏了一些非常基本的东西,我希望有人能指出:-)。


共有1个答案

焦同
2023-03-14

事实证明,有专门的构造,但需要一些时间来找到它。如果您需要有一个组件,该组件有一个包含未满足注入的模块——让它成为@子组件。正如留档明确指出:

该关系允许子组件实现在声明时从其父级继承整个绑定图。因此,在子组件与父组件关联之前,不会评估其完整性

所以在我的例子中,我的库项目需要是一个匕首子组件。当我在我的应用程序项目中使用它时,我的应用程序dagger组件必须包含lib子组件。

代码:

库子组件:

@Subcomponent(modules = Mod1.class)
public interface MyLibraryComponent {
    void inject(Mod1Interface1 in);
}

应用程序组件:

@Component(modules = Mod2.class)
@Singleton
public interface MyAppComponent {
    void inject(MainActivity act);
    MyLibraryComponent newMyLibraryComponent();
}

请注意MyLibraryComponent newMyLibraryComponent();-这就是你告诉dagger你的组件包含那个子组件的方式。

图形实例化:

    MyAppComponent comp = DaggerMyAppComponent.builder().build();

请注意,在这种情况下,与使用具有依赖项的组件组合相反(@component's property),您不必“手动”构造子组件。如果子组件的模块不需要特殊配置(即构造函数参数),组件将“自动”处理该问题。如果某个子组件的模块需要配置,您可以通过如下组件实例化进行配置:

MyAppComponent comp = DaggerMyAppComponent.builder().
                          mod2(new Mod2SpecialConfiguration()).
                          build();

对于android,如果您的库项目包含活动,则会有一个特殊的转折点,因为每个活动都必须“按需”单独注入,而常规java应用程序通常在启动时注入整个应用程序一次。

为了举例,假设我们的库项目包含登录活动“ActLogin”,我们在几个应用程序中使用它。

@Subcomponent(modules = Mod1.class)
public interface MyLibraryComponent {
    void injectActLogin(ActLogin act);
    void inject(Mod1Interface1 in);
}

问题是,在Android中,我们通常在应用程序对象中创建依赖关系图,如下所示:

public class MyApplication extends Application {
    private MyAppComponent mAppDependencyInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        mAppDependencyInjector = DaggerMyAppComponent.builder().build();
    }

    public MyAppComponent getAppDependencyInjector() {
        return mAppDependencyInjector;
    }
}

然后在你的活动中,你可以这样使用它:

@Override
protected void onCreate(Bundle savedInstanceState) {
    // ...
    ((MyApplication) getApplication()).getAppDependencyInjector().inject(this);
    // ...
}

但是我们的ActLogin活动是库项目(和匕首组件)的一部分,它不知道它将在什么应用程序中使用,所以我们如何注入它?

有一个很好的解决方案,但请注意,我不确定它是否规范(即,文档中没有提到,它不是“权威机构”(afaik)给出的示例)

项目的源代码可以在github上找到。

首先,您必须扩展应用程序组件中的库匕首组件:

public interface MyAppComponent extends MyLibraryComponent {

这样,您的应用程序组件将包含子组件中的所有inject方法,因此您也将能够注入它的活动。毕竟,顶层组件实际上是整个对象图(更准确地说,Dagger生成的DaggerMyAppComponent代表整个图),因此它能够将自己定义的所有内容注入所有子组件中。

现在我们必须确保库项目能够访问它。我们创建一个助手类:

public class MyLibDependencyInjectionHelper {
    public static MyLibraryComponent getMyLibraryComponent(Application app) {
        if (app instanceof MyLibraryComponentProvider) {
            return ((MyLibraryComponentProvider) app).getMyLibraryComponent();
        } else {
            throw new IllegalStateException("The Application is not implementing MyLibDependencyInjectionHelper.MyLibraryComponentProvider");
        }
    }


    public interface MyLibraryComponentProvider {
        MyLibraryComponent getMyLibraryComponent();
    }
}

然后我们必须在应用程序类中实现MyLibraryComponentProvider

public class MyApplication extends Application implements
    MyLibDependencyInjectionHelper.MyLibraryComponentProvider {
    // ...

    @Override
    public MyLibraryComponent getMyLibraryComponent() {
        return (MyLibraryComponent) mAppDependencyInjector;
    }
}

在ActLogin中,我们注入:

public class ActLogin extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);
        // ...
        MyLibDependencyInjectionHelper.getMyLibraryComponent(getApplication()).
                           injectActLogin(this);
        // ...
    }
}

此解决方案存在一个问题:如果忘记在应用程序中实现MyLibraryComponentProvider,则在编译时不会出现错误,而是在运行时启动ActLogin活动时出现错误。幸运的是,这可以通过简单的单元测试轻松避免。

 类似资料:
  • 我正在使用Dagger 2,在为我的类实现测试时,模块中的generate singleton providers存在一些问题。 所以我有两个问题。 > 我不能使用构造函数注入,因为有一个默认的构造函数。如何从测试模块中获取Foo? 在和中,如何确保在运行每个测试时创建的新实例? 谢谢

  • 我想在单元测试模块中使用我的领域管理器。我做的 然后我想实现 但是realmManager为空。如何使用匕首2编写简单的模块测试?我用了匕首模拟,但没有用。我的模块包含 我尝试了谷歌的一切,但我不知道如何从图形中注入对象。

  • 这很好,但是如果我使用全局组件甚至子组件中的一个模块,那么应该传入上下文。因此,这意味着如果我用匕首注入演示器,它将被绑定到ApplicationContext。这使得作为JUnit进行测试变得困难。Android代码不应该在演示器中。 所以我想问的是,最好的做法是只在活动、片段、广播接收器和服务中使用匕首吗?就mvp架构而言,这就是。另一个解决方案是设计另一个dagger组件,但不是与appco

  • 我找到了静态编程语言Lazy对象的答案,在这里使用:静态编程语言:检查Lazy val是否已初始化 但看起来像匕首。Lazy没有相同的公共方法。 这就是我如何懒洋洋地使用Dagger注射: 如何在不调用someService的情况下检查someService是否已初始化。get()哪个将初始化它?除了引入布尔标志并自己跟踪它之外。。 谢谢

  • 我不熟悉匕首和莫基托。我尝试在单元测试中使用Dagger模块中定义的构造函数,以便使用默认值创建对象。 这是模块: @模块类自动关闭倒计时模块{ 这就是我在单元测试中模拟AutoCloseCountDown类的方法: @RunWith(MockitoJUnitRunner.class)公共类AutoCloseCountDownTimerTest{ 如何实现自动关闭的CountDownTimer将在

  • 我正在尝试为我的navControl ler提供匕首柄。但是我使用的方法不起作用。我想提供navControl ler以便通过构造函数注入将其注入我的片段中。 我还创建了FragmentWorks类,以便通过构造函数注入我的依赖项。我知道是这里的问题,但我不知道正确的解决方案… 感谢您的帮助,谢谢! 原因:java.lang.ClassCastException:com.example.app。应