简体   繁体   English

Android @Singleton上的Dagger 2注释类没有被注入

[英]Dagger 2 on Android @Singleton annotated class not being injected

I am currently trying to integrate Dagger 2 into an Android application. 我目前正在尝试将Dagger 2集成到Android应用程序中。 My project setup is as follows: 我的项目设置如下:

  • 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: 现在 - 例如在我的片段或活动或常规类中,我将按如下方式注入上述Singleton:

public class SomeClass{

  @Inject
  MyManager myManager;
}

Or so I thought, because in practice myManager is always null. 或者我想,因为在实践中,myManager总是为空。 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类的目的是成为一个应用程序范围的可访问组件累积实体 - 这就是我选择@Singleton的原因。

UPDATE 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相关的代码。

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在库项目中:

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

Also in the library project is the bootstrap class: 库项目中还有bootstrap类:

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

Then I use the above Bootstrap class in my activity (in my concrete app, NOT in 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, ie 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. 如果我没有误读,暗示我需要在Bootstrap类中指定将使用@Inject注入内容的每个类。 Sadly - this is not an option, as I have more than 40 classes and activities for which I'd have to do that. 可悲的是 - 这不是一个选择,因为我有超过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? 另外:如果我忘了在这里指定我的40多个课程之一,似乎没有编译时检查? It just wont work - ie crash the app at runtime. 它不会工作 - 即在运行时崩溃应用程序。

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. 在您的Activity中,因为范围不在多个组件实例之间共享。 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 Bootstrap as injector , and _Bootstrap as ApplicationComponent , so it looks like this: 我个人命名Bootstrapinjector ,并_BootstrapApplicationComponent ,所以它看起来是这样的:

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. 编辑:对于您的上一个问题,您可以通过subscoping和组件依赖性来解决这个问题。

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);

It's hard to say what your problem was, since you didn't show what your Component looks 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 MainComponent depends 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. 请注意, MainComponent依赖于LibraryComponent ,但由于后者具有单例范围,因此您需要为另一个范围定义范围,我在这里使用了“活动范围”。 (Or you could also just make the MainComponent a singleton and get rid of the LibraryComponent completely if that suits your needs.) (或者您也可以将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 @Singleton and @Inject annotations on the two classes listed ( MyManager and SomeClass ), and there is no other Dagger-related code in your project. 如果我正确理解你的设置,你到目前为止只在所列的两个类( MyManagerSomeClass )上使用@Singleton@Inject注释,并且项目中没有其他与Dagger相关的代码。

In that case, the reason your MyManager isn't getting injected, is because Dagger doesn't know how to provide/instantiate the dependencies. 在这种情况下, MyManager未被注入的原因是因为Dagger不知道如何提供/实例化依赖项。 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. 如果没有任何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 MyManager injected into SomeClass : 我不知道您是否有使用依赖注入概念的经验,但假设您没有,我将逐步介绍您需要了解的最小基础知识,以便将MyManager注入SomeClass

First: when you use DI, you need to understand the difference between "newables" and "injectables". 第一:当你使用DI时,你需要了解“newables”和“injectables”之间的区别。 This blogpost by Misko Hevery explains the details. 这篇由Misko Hevery撰写的博文解释了细节。

This means, you can't new up your SomeClass . 这意味着,你不能new你的SomeClass This won't work: 这不起作用:

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 SomeClass and it has no opportunity to inject anything. 因为如果你这样做(比如在一个活动或片段中),Dagger将不知道你期望一个依赖项被注入SomeClass并且它没有机会注入任何东西。

In order for its dependencies to get injected, you have to instantiate (or inject) SomeClass itself through Dagger too. 为了使其依赖项被注入,您还必须通过Dagger实例化(或注入) SomeClass本身。

In other words, say in your Activity where SomeClass is used, you'll need: 换句话说,在你的Activity中使用SomeClass ,你需要:

@Inject
SomeClass mSomeClass;

Next, you need a Dagger component to perform the actual injection. 接下来,您需要一个Dagger组件来执行实际注入。 To create a component, you create an interface with a method that takes your root object (say MainActivity ) as argument, eg: 要创建组件,可以使用将根对象(例如MainActivity )作为参数的方法创建一个接口,例如:

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

Now when you build your project, Dagger 2 generates a class called DaggerBootstrap that implements this interface. 现在,当您构建项目时,Dagger 2会生成一个名为DaggerBootstrap的类来实现此接口。 You use this generated class to perform the injection, say in your activity's onCreate: 您可以使用此生成的类来执行注入,例如在您的活动的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 Activity base class, but not for your actual concrete activity. 似乎最后缺失的部分是你的组件为Activity基类提供了一个注入方法,但不是为你的实际具体活动提供了注入方法。

Unfortunately Dagger 2 requires an inject method for each activity 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基类注入

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()可能不是您想要的,因为该组件应该提供您的单例,所以您可能最好从其他地方获取现有实例,例如从Application实例获取。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM