java 未注入 Android @Singleton 注释类上的 Dagger 2

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31417795/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 18:35:35  来源:igfitidea点击:

Dagger 2 on Android @Singleton annotated class not being injected

javaandroiddaggerdagger-2

提问by AgentKnopf

I am currently trying to integrate Dagger 2 into an Android application. My project setup is as follows:

我目前正在尝试将 Dagger 2 集成到 Android 应用程序中。我的项目设置如下:

  • library
  • app (depends on library)
  • 图书馆
  • 应用程序(取决于库)

In my library project I defined a class that I'll later inject into other classes that need it (Activities and regular classes) in the library as well as the app project.

在我的库项目中,我定义了一个类,稍后我会将其注入到库中需要它的其他类(活动和常规类)以及应用程序项目中。

@Singleton
public class MyManager{
  @Inject
  public MyManager(){
    //Do some initializing
  }
}

Now - for instance in my Fragments or Activities or regular classes I'd inject the above Singleton as follows:

现在 - 例如在我的片段或活动或常规课程中,我将按如下方式注入上述单例:

public class SomeClass{

  @Inject
  MyManager myManager;
}

Or so I thought, because in practice myManager is always null. And apparently it's constructor is never called either, so I guess I must be missing something configuration-wise? Or maybe I misunderstood the documentation and it's not meant to work this way at all? The purpose of MyManager class is to be an application-wide accessible component-accumulating entity - that's why I went for the @Singleton.

或者我是这么想的,因为在实践中 myManager 总是为空。显然它的构造函数也从未被调用过,所以我想我一定在配置方面遗漏了一些东西?或者也许我误解了文档,它根本不打算以这种方式工作?MyManager 类的目的是成为一个应用程序范围内可访问的组件累积实体——这就是我选择@Singleton 的原因。

UPDATE

更新

To avoid confusion: I mentioned my having components somewhere in a comment I think - this refers to components in the sense of "component based design" and has nothing to do with dagger. The dagger-based code I have is all listed above - there is nothing else related to dagger in my code.

为避免混淆:我在我认为的评论中提到了我的组件 - 这是指“基于组件的设计”意义上的组件,与匕首无关。我拥有的基于 dagger 的代码都在上面列出了 - 我的代码中没有其他与 dagger 相关的内容。

When I started adding @Component I had some compiler issues, because my dagger2 was not setup properly - check out this really helpful thread on how to setup dagger2 correctly: https://stackoverflow.com/a/29943394/1041533

当我开始添加 @Component 时,我遇到了一些编译器问题,因为我的 dagger2 没有正确设置 - 查看这个关于如何正确设置 dagger2 的非常有用的线程:https://stackoverflow.com/a/29943394/1041533

UPDATE 2

更新 2

Here is my updated code, based on G. Lombard's suggestions - I changed the code as follows - the original Singleton is in the library project:

这是我根据 G. Lombard 的建议更新的代码 - 我将代码更改如下 - 原始单例在库项目中:

@Singleton
public class MyManager{
  @Inject
  public MyManager(){
    //Do some initializing
  }
}

Also in the library project is the bootstrap class:

库项目中还有引导类:

@Singleton
@Component
public interface Bootstrap {
    void initialize(Activity activity);
}

Then I use the above Bootstrap class in my activity (in my concrete app, NOTin the library project! I do however also have Classes/Activities in the library that'll access Bootstrap to inject MyManager):

然后我在我的活动中使用上面的 Bootstrap 类(在我的具体应用程序中,而不是在库项目中!但是我在库中也有可以访问 Bootstrap 以注入 MyManager 的类/活动):

public class MyActivity extends Activity{

    @Inject
    MyManager manager;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //DONT DO THIS !!! AS EXPLAINED BY EpicPandaForce
        DaggerBootstrap.create().initialize(this);
    }
}

But even after this line:

但即使在这一行之后:

        DaggerBootstrap.create().initialize(this);

the manager instance is still null, i.e. not injected.

管理器实例仍然为空,即未注入。

I just found this: https://stackoverflow.com/a/29326023/1041533

我刚刚发现这个:https: //stackoverflow.com/a/29326023/1041533

Which if I don't misread, implies I need to specify every single class in the Bootstrap class that will use @Inject to have stuff injected. Sadly - this is not an option, as I have more than 40 classes and activities for which I'd have to do that.

如果我没有误读,这意味着我需要指定 Bootstrap 类中的每个类,这些类将使用 @Inject 来注入内容。可悲的是 - 这不是一个选择,因为我有 40 多个课程和活动,我必须这样做。

Meaning my Bootstrap interface apparently would have to look something like this:

这意味着我的 Bootstrap 界面显然必须看起来像这样:

@Singleton
@Component
public interface Bootstrap {
    void initialize(ActivityA activity);
    void initialize(ActivityB activity);
    void initialize(ActivityC activity);
    void initialize(ActivityD activity);
    void initialize(ActivityE activity);
    void initialize(ActivityF activity);
    //and so on and so forth...
}

If the above is true, that would not be worth it for my use case. Plus: Seems there is no compile-time check, if I forgot to specify one of my 40+ classes here? It just wont work - i.e. crash the app at runtime.

如果上述情况属实,那对于我的用例来说是不值得的。另外:似乎没有编译时检查,如果我忘记在这里指定我的 40 多个类之一?它只是行不通 - 即在运行时使应用程序崩溃。

采纳答案by EpicPandaForce

You're making a mistake in that you are using

你犯了一个错误,因为你正在使用

DaggerBootstrap.create().initialize(this);

in your Activity, as scopes are not shared across multiple component instances. What I recommend is using a custom application class

在您的活动中,因为范围不会在多个组件实例之间共享。我推荐的是使用自定义应用程序类

public class CustomApplication extends Application {
    @Override
    public void onCreate() {
         super.onCreate();
         Bootstrap.INSTANCE.setup();
    }
}

@Component
@Singleton
public interface _Bootstrap {
    void initialize(ActivityA activityA);
    //void initiali...
}

public enum Bootstrap {
    INSTANCE;

    private _Bootstrap bootstrap;

    void setup() {
        bootstrap = Dagger_Bootstrap.create();
    }

    public _Bootstrap getBootstrap() {
        return bootstrap;
    }
}

Then you could call it as

那么你可以称之为

Bootstrap.INSTANCE.getBootstrap().initialize(this);

This way, you share the component across your classes. I personally named Bootstrapas injector, and _Bootstrapas ApplicationComponent, so it looks like this:

这样,您就可以跨类共享组件。我个人命名Bootstrapinjector,并_Bootstrap作为ApplicationComponent,所以它看起来是这样的:

Injector.INSTANCE.getApplicationComponent().inject(this);

But that's just my typical setup. Names don't really matter.

但这只是我的典型设置。名字真的不重要。

EDIT: To your last question, you can solve this by subscoping and component dependencies.

编辑:对于你的最后一个问题,你可以通过子范围和组件依赖来解决这个问题。

Your library project should be able to see only the library classes, correct? In that case, all you do is

您的库项目应该只能看到库类,对吗?在这种情况下,你要做的就是

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface LibraryScope {
}

@Component(modules={LibraryModule.class})
@LibraryScope
public interface LibraryComponent {
    LibraryClass libraryClass(); //provision method for `MyManager`
}

@Module
public class LibraryModule {
    @LibraryScope
    @Provides
    public LibraryClass libraryClass() { //in your example, LibraryClass is `MyManager`
        return new LibraryClass(); //this is instantiation of `MyManager`
    }
}

public enum LibraryBootstrap {
    INSTANCE;

    private LibraryComponent libraryComponent;

    static {
        INSTANCE.libraryComponent = DaggerLibraryComponent.create();
    }

    public LibraryComponent getLibraryComponent() {
        return libraryComponent;
    }
}

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {
}

@Component(dependencies={LibraryComponent.class}, modules={AdditionalAppModule.class})
@ApplicationScope
public interface ApplicationComponent extends LibraryComponent {
    AdditionalAppClass additionalAppClass();

    void inject(InjectableAppClass1 injectableAppClass1);
    void inject(InjectableAppClass2 injectableAppClass2);
    void inject(InjectableAppClass3 injectableAppClass3);
}

@Module
public class AdditionalAppModule {
    @ApplicationScope
    @Provides
    public AdditionalAppClass additionalAppClass() { //something your app shares as a dependency, and not the library
        return new AdditionalAppClass();
    }
}

public enum ApplicationBootstrap {
    INSTANCE;

    private ApplicationComponent applicationComponent;

    void setup() {
        this.applicationComponent = DaggerApplicationComponent.builder()
                                        .libraryComponent(LibraryBootstrap.INSTANCE.getLibraryComponent())
                                        .build();
    }

    public ApplicationComponent getApplicationComponent() {
        return applicationComponent;
    }
}

Then

然后

@Inject
LibraryClass libraryClass; //MyManager myManager;

...
    ApplicationBootstrap.INSTANCE.getApplicationComponent().inject(this);

回答by G. Lombard

It's hard to say what your problem was, since you didn't show what your Componentlooks like and whether you have multiple components etc.

很难说你的问题是什么,因为你没有展示你的Component样子以及你是否有多个组件等。

Assuming this logical structure:

假设这个逻辑结构:

/app
   MainComponent
   SomeClass    // where MyManager is to be injected
   MainActivity // where SomeClass is to be injected
/library
   LibraryComponent
   MyManager    // Singleton

Then your classes as listed would inject correctly with the following configuration:

然后,您列出的类将使用以下配置正确注入:

@Singleton
@Component
public interface LibraryComponent {
    MyManager getMyManager();
}

and the app-level component to inject dependencies into the activity:

以及将依赖项注入活动的应用级组件:

@ActivityScope
@Component(dependencies = LibraryComponent.class)
public interface MainComponent {
    void inject(MainActivity mainActivity);
}

Note that MainComponentdepends on LibraryComponent, but because the latter has singleton scope, you need to define a scope for the other one too, which I was I used the "activity scope" here. (Or you could also just make the MainComponent a singleton and get rid of the LibraryComponent completely if that suits your needs.)

请注意,这MainComponent取决于LibraryComponent,但因为后者具有单例作用域,所以您也需要为另一个作用域定义一个作用域,我在这里使用了“活动作用域”。(或者,如果满足您的需要,您也可以将 MainComponent 设为单例并完全摆脱 LibraryComponent。)

Finally it's all injected into the activity like this:

最后,它全部注入到活动中,如下所示:

@Inject
SomeClass someClass;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    DaggerMainComponent.builder()
            .libraryComponent(DaggerLibraryComponent.create())
            .build()
            .inject(this);

    someClass.doSomething();
}

I've put a working sample here on GitHub

在 GitHub 上放了一个工作示例

Update 1:

更新 1:

If I understand your setup correctly, you've so far only used the @Singletonand @Injectannotations on the two classes listed (MyManagerand SomeClass), and there is no other Dagger-related code in your project.

如果我正确理解了您的设置,那么到目前为止您只在列出的两个类(和)上使用了@Singleton@Inject注释,并且您的项目中没有其他与 Dagger 相关的代码。MyManagerSomeClass

In that case, the reason your MyManagerisn't getting injected, is because Dagger doesn't know how to provide/instantiate the dependencies. This is where the "components" come in that I mentioned above. Without any Dagger 2 components (interface or abstract class annotated with @Component), your dependencies won't get injected automatically.

在这种情况下,您MyManager没有被注入的原因是因为 Dagger 不知道如何提供/实例化依赖项。这就是我上面提到的“组件”的来源。如果没有任何 Dagger 2 组件(用 注释的接口或抽象类@Component),您的依赖项将不会被自动注入。

I don't know if you have experience with Dependency Injection concepts, but assuming you don't, I'll step through the minimum basics you'll need to understand to get your MyManagerinjected into SomeClass:

我不知道您是否有使用依赖注入概念的经验,但假设您没有,我将逐步介绍您需要了解的最基本的基础知识,以便将您MyManager注入SomeClass

First: when you use DI, you need to understand the difference between "newables" and "injectables". This blogpostby Misko Hevery explains the details.

第一:当你使用DI时,你需要了解“newables”和“injectables”之间的区别。此博文由MISKO Hevery解释细节。

This means, you can't newup your SomeClass. This won't work:

这意味着,您无法new升级您的SomeClass. 这行不通:

mSomeClass = new SomeClass();

Because if you did that (say in an activity or fragment), Dagger will have no idea that you expected a dependency to get injected into SomeClassand it has no opportunity to inject anything.

因为如果你这样做了(比如在一个活动或片段中),Dagger 将不知道你期望一个依赖项被注入SomeClass并且它没有机会注入任何东西。

In order for its dependencies to get injected, you have to instantiate (or inject) SomeClassitself through Dagger too.

为了注入它的依赖项,您也必须SomeClass通过 Dagger实例化(或注入)自身。

In other words, say in your Activity where SomeClassis used, you'll need:

换句话说,在您的 Activity 中SomeClass使用的地方说,您需要:

@Inject
SomeClass mSomeClass;

Next, you need a Dagger component to perform the actual injection. To create a component, you create an interface with a method that takes your root object (say MainActivity) as argument, e.g.:

接下来,您需要一个 Dagger 组件来执行实际注入。要创建一个组件,您需要创建一个接口,该接口的方法将您的根对象(例如MainActivity)作为参数,例如:

@Singleton
@Component
public interface Bootstrap {
    void initialize(MainActivity activity);
}

Now when you build your project, Dagger 2 generates a class called DaggerBootstrapthat implements this interface. You use this generated class to perform the injection, say in your activity's onCreate:

现在,当您构建项目时,Dagger 2 会生成一个名为DaggerBootstrap实现此接口的类。你使用这个生成的类来执行注入,在你的活动的 onCreate 中说:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    DaggerBootstrap.create().initialize(this);

    mSomeClass.doSomething();
}

I believe this generated component is the key part you're missing. Full code for above here.

我相信这个生成的组件是您缺少的关键部分。上面的完整代码在这里

Some useful Dagger 2 resources:

一些有用的 Dagger 2 资源:

Update 2:

更新 2:

Seems like the final missing piece of the puzzle was that your component provided an inject method for the Activitybase class, but not for your actual concrete activity.

似乎拼图的最后一个缺失部分是您的组件为Activity基类提供了一个注入方法,但没有为您的实际具体活动提供。

Unfortunately Dagger 2 requires an inject method for eachactivity or other class you want to inject into.

不幸的是,Dagger 2 需要为您想要注入的每个活动或其他类提供一个注入方法。

As you mentioned, this will be annoying when you have many different activities in your app. There some possible workarounds for this, search for "dagger 2 inject base class", for example this suggestion by @EpicPandaForce: Dagger 2 base class injections

正如您所提到的,当您的应用程序中有许多不同的活动时,这会很烦人。有一些可能的解决方法,搜索“dagger 2注入基类”,例如@EpicPandaForce的这个建议:Dagger 2 base class injections

Also note, as pointed out by @EpicPandaForce in the comments, that in my simplistic example I called DaggerLibraryComponent.create()every time which is probably not what you want, since that component is supposed to provide your singletons, so you're probably better off getting the existing instance from somewhere else such as from your Application instance.

另请注意,正如@EpicPandaForce 在评论中指出的那样,在我DaggerLibraryComponent.create()每次调用的简单示例中,这可能不是您想要的,因为该组件应该提供您的单例,因此您最好获取现有的来自其他地方的实例,例如来自您的应用程序实例。