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

2022-09-04 02:25:41

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

  • 图书馆
  • 应用程序(取决于库)

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

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

现在 - 例如,在我的片段或活动或常规类中,我会注入上面的单例,如下所示:

public class SomeClass{

  @Inject
  MyManager myManager;
}

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

更新

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

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

更新 2

以下是我根据G. Lombard的建议更新的代码 - 我更改了代码如下 - 原始的Singleton在库项目中:

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

在库项目中还有引导类:

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

然后,我在我的活动中使用上面的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);
    }
}

但即使在这条线之后:

        DaggerBootstrap.create().initialize(this);

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

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

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

这意味着我的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...
}

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


答案 1

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

DaggerBootstrap.create().initialize(this);

,因为作用域不在多个组件实例之间共享。我建议使用自定义应用程序类

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

然后你可以称之为

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

这样,您就可以在类之间共享组件。我个人命名为 , 和 as ,所以它看起来像这样:Bootstrapinjector_BootstrapApplicationComponent

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

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

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

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

@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;
    }
}

然后

@Inject
LibraryClass libraryClass; //MyManager myManager;

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

答案 2

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

假设这个逻辑结构:

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

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

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

和应用级组件,用于将依赖项注入到活动中:

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

请注意,这取决于 ,但是由于后者具有单例作用域,因此您也需要为另一个作用域定义一个作用域,我在这里使用了“活动作用域”。(或者,您也可以将 MainComponent 设置为单例,如果这符合您的需要,则可以完全删除 LibraryComponent。MainComponentLibraryComponent

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

@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();
}

我已经在GitHub上放了一个工作示例

更新 1:

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

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

我不知道你是否有依赖注入概念的经验,但假设你没有,我将逐步介绍你需要理解的最低限度的基础知识,以便将你注入:MyManagerSomeClass

首先:当你使用DI时,你需要了解“newables”和“injectables”之间的区别。Misko Hevery的这篇博客文章解释了细节。

这意味着,您无法提升.这不起作用:newSomeClass

mSomeClass = new SomeClass();

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

为了让它的依赖关系被注入,你也必须通过Dagger实例化(或注入)自身。SomeClass

换句话说,在“活动记录”中使用的位置,您需要:SomeClass

@Inject
SomeClass mSomeClass;

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

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

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

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

    DaggerBootstrap.create().initialize(this);

    mSomeClass.doSomething();
}

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

一些有用的匕首2资源:

更新 2:

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

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

如前所述,当您的应用程序中有许多不同的活动时,这将很烦人。对此有一些可能的解决方法,搜索“匕首2注入基类”,例如@EpicPandaForce的建议:匕首2基类注入

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


推荐