如何迁移缺少模块具有完整=假,从匕首1至2匕首注入匕首、模块、完整

2023-09-05 00:35:05 作者:别等时光非礼了梦想

我有一个都使用Android应用程序和常规的Java应用程序库项目/模块。 在匕首1本项目/模块属性完整= FALSE 。内有一个不受任何类实现或@Provides方法满足一个@Inject字段。这个想法很给力顶模块(S),其中有完整=真来提供系统的具体实施

I have a library project/module that is used by both Android apps and regular java apps. In Dagger 1 this project/module has property complete = false. Within there is an @Inject field that is not satisfied by any class implementation or @Provides method. The idea is to force the "top" module(s) which has complete = true to provide system specific implementation

只是出于示例的缘故:在库项目中,我有ActLogin的活动,有现场 @注入@Named(应用程序版本)mAppVersion 。该字段的值被登录到服务器时使用。 ActLogin是由使用该库的几个应用程序。每个应用程序的模块有完整=真,并提供价值与 @Provides @Named(应用程序版本)provideAppVersion()

Just for the sake of example: In the library project I have ActLogin activity that have field @Inject @Named("app version") mAppVersion. The value of this field is used when logging in into a server. ActLogin is used by several apps that use this library. Each app's module has complete = true and provides value with @Provides @Named("app version") provideAppVersion()

文档匕首2的迁移( http://google.github.io /dagger/dagger-1-migration.html )规定:

Documentation for migration of Dagger 2 (http://google.github.io/dagger/dagger-1-migration.html) states:

匕首2个模块都声明为完整= FALSE 和库= TRUE

Dagger 2 modules are all declared as complete = false and library = true

而在同一时间的主文档页面( http://google.github.io/dagger / )规定:

and in the same time the "main" documentation page (http://google.github.io/dagger/) states:

匕首标注处理器是严格,会导致编译器错误,如果任何绑定无效或不完整

The Dagger annotation processor is strict and will cause a compiler error if any bindings are invalid or incomplete.

后者显然是正确的,因为试图建立与不满意的注射错误时产生(错误:java.lang.String中不能没有一个@ Provides-或@生成标注方法提供的)。

The latter is obviously the correct one because when trying to build with unsatisfied inject error is produced (error: java.lang.String cannot be provided without an @Provides- or @Produces-annotated method).

现在的问题是:是否有可能这种方法(推迟提供注入)迁移到匕首2,如何

The question is: is it possible to migrate this approach (deferring providing inject) to Dagger 2 and how?

P.S。最初,我还是那句话看作是一个肮脏的解决方法是提供一种在图书馆的@Module一些虚拟的值,但是 - 你不能有模块覆盖匕首2(这是一种跆拳道(!!!)模块覆盖是最有用的功能对我来说。创建单元测试时)。也许我失去了一些东西很基本的,我希望有人能指出来: - )

P.S. Initially I thought as a dirty workaround to provide some dummy values in the library's @Module but then again - you cannot have module overrides in Dagger 2 (which is kind of WTF(!!!). Module overrides were the most useful feature for me when creating unit tests). Probably I am missing something very basic and I hope that someone can point it out :-).

推荐答案

事实证明,有专门构建了这一点,但它需要一些时间来找到它。 如果你需要有一个有一个包含不满意注入(S)模块的组成部分 - 使其@Subcomponent。由于文件明确规定:

It turns out that there is dedicated construct for this but it takes some time to find it out. If you need to have a component that have a module which contains unsatisfied inject(s) - make it @Subcomponent. As documentation clearly states:

这关系使得子实施继承其父完整的结合图形,当它被宣布。出于这个原因,一个子组件未评估完整性,直到它与父相关联的

手绘刀 匕首 军刺

That relationship allows the subcomponent implementation to inherit the entire binding graph from its parent when it is declared. For that reason, a subcomponent isn't evaluated for completeness until it is associated with a parent

所以,在我的情况,我的库项目必须是匕首子。当我在我的应用程序项目中使用它,我的应用程序的匕首组件必须包括的lib子。

So in my case, my library project needs to be a dagger subcomponent. When I use it in my app project, my app dagger component have to include the lib subcomponent.

在code:

库子:

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

该应用程序组件:

The app component:

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

请注意, MyLibraryComponent newMyLibraryComponent(); - 那你是怎么知道的匕首,你的组件包含子

Please note the MyLibraryComponent newMyLibraryComponent(); - that is how you tell dagger that your component contains that subcomponent.

图表实例:

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

请注意,相反,使用成分组成与依赖(@组件的属性),在这种情况下,你不必为手动构建你的子组件。该组件将自动照顾的,若子组件的模块并不需要特殊配置(即构造函数的参数)。在某些情况下,子组件的模块需要配置你这样做槽组件实例是这样的:

Please note that contrary to using component composition with dependencies (@Component's property) in this case you don't have to "manually" construct your subcomponent. The component will "automatically" take care for that in case the subcomponent's modules don't need special configuration (i.e. constructor parameters). In case some subcomponent's module requires configuration you do it trough the component instantiation like this:

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

对于Android有一个特殊的扭曲,如果您的库项目中包含的活动,因为每个活动都单独注入按需违反常规的Java应用程序,你通常会在启动时注入整个应用程序一次。

For android there is a special twist if your library project contains activities because each activity have to be separately injected "on demand" contrary to regular java application where you usually inject the whole application once at start up time.

为了举例假设我们的图书馆项目包含登录活动ActLogin我们使用的是常见的几个应用程序。

For the sake of example let's say our library project contains login activity "ActLogin" that we use as common for several applications.

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

问题是,在Android中,我们通常会创建我们的依赖关系图在这样的适用对象:

The problem is that in Android we usually create our dependency graph in the Application object like this:

public class MyApplication extends Application {
    private MyAppComponent mAppDependencyInjector;

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

    public MyAppComponent getAppDependencyInjector() {
        return mAppDependencyInjector;
    }
}

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

and then in your activity you use it like this:

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

但我们的 ActLogin 活动是库项目(和匕首成分)的一部分,不是事件知道什么应用程序将它让我们如何使用注资呢?

but our ActLogin activity is part of the library project (and dagger component) that is not event aware of what application will it be used in so how are we going to inject it?

有一个很好的解决方案,但请注意,我不知道这是典型(即,它不会在文档中提到的,它不是作为例子给出的权威(AFAIK ))

There is a nice solution but please note that I am not sure it is canonical (i.e. it is not mentioned in the documentation, it is not given as an example by the "authorities" (afaik))

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

首先,你必须将库匕首组件延长你的应用程序组件:

First you will have to extend the library dagger component in you app component:

public interface MyAppComponent extends MyLibraryComponent {

这样,你的应用程序组件将包含所有的从子方法,这样你就能够注入它的活动了。毕竟,顶级组件其实是在整个对象图(更多precisely匕首产生DaggerMyAppComponent重新present全图),所以它能够在自己+在所有子定义的一切都注入。

That way your app component will contain all the inject methods from the subcomponent so you will be able to inject it's activities too. After all, top component is in fact the whole object graph (more precisely the Dagger generated DaggerMyAppComponent represent the whole graph) so it is able to inject everything defined in itself + in all subcomponents.

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

Now we have to assure that the library project is able to access it. We create a helper class:

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我们注入:

and in ActLogin we inject:

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

没有与此解决方案的一个问题:如果你忘了贯彻落实 MyLibraryComponentProvider 在应用程序中,你不会得到一个错误在编译时间,但在运行时,当您启动ActLogin活动。幸运的是,可以很容易地避免使用简单的单元测试。

There is a problem with this solution: If you forget to implement the MyLibraryComponentProvider in your application you will not get an error at compile time but at runtime when you start ActLogin activity. Luckily that can be easily avoided with simple unit test.