注射后为什么我的字段为“null”?我如何注射我的物体?

时间:2018-05-10 10:05:32

标签: android dagger-2

  

这是Canonical Question,因为Dagger 2对对象初始化存在很多误解。

     

如果您的问题被标记为重复,请仔细阅读此文章,并确保了解构造函数注入和字段注入之间的区别。

我尝试向我的演示者注入Context,但在尝试使用它时会出现NullPointerException。

class MyPresenter {

  @Inject Context context;

  private MyView view;

  @Inject
  MyPresenter(MyView view) {
    this.view = view;
  }
}

我的模块看起来像这样

@Module
class MyModule {

  @Provides
  MyPresenter provideMyPresenter(MyView view) {
    return new MyPresenter(view);
  }
}

我在此活动中注入了演示者:

class MyActivity extends Activity {

  @Inject MyPresenter presenter;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    createMyActivityComponent().inject(this);
  }
}

2 个答案:

答案 0 :(得分:5)

以上内容包括构造函数和字段注入,但既未正确。如果我们从@Inject删除了所有MyPresenter注释,则该示例的行为会相同,因为我们并未使用其中的任何注释。

@Provides
MyPresenter provideMyPresenter(MyView view) {
  // no constructor injection, we create the object ourselves!
  return new MyPresenter(view);
}

// also no mention anywhere of component.inject(presenter)
// so the fields won't be injected either

确保使用 构造函数注入字段注入。混合两者通常表示您的设置或理解中存在错误。

    字段上的
  • @Inject字段注入
  • 的标记 构造函数上的
  • @Inject构造函数注入的标记

这意味着您的班级应该

  • 构造函数中的单个 @Inject,或
  • {strong> 所有要初始化的字段,但无构造函数

不要在任何地方洒@Inject并希望事情有效!确保将注释放在需要的地方。不要混合场和构造函数注入!

构造函数注入应该优于字段注入,因为它创建了一个初始化和可用的对象。字段注入将与框架组件一起使用,其中框架创建对象。当您尝试使用它们时,您必须手动调用@Inject进行字段注入,或者任何带注释的字段将为空

构造函数注入

顾名思义,您将依赖项作为参数 置于构造函数 中。构造函数上的注释告诉Dagger关于该对象,然后它可以通过使用所有必需的依赖项调用它来为您创建对象。 Dagger也会在创建对象后注入任何带注释的字段或方法,但普通构造函数注入通常应该受到青睐,因为它不会隐藏任何依赖项。

创建对象的Dagger也意味着不需要模块中的component.inject(object)方法来创建对象。您需要做的就是将@Provides添加到构造函数并声明依赖项。

@Inject

如果要将实现绑定到接口,则仍然无需自己创建对象。

class MyPresenter {

  private Context context;
  private MyView view;

  @Inject
  MyPresenter(MyView view, Context context) {
    this.view = view;
    this.context = context
  }
}

甚至还有一个更短(更高性能)的上述用例版本:

@Module class MyModule {

  @Provides
  MyPresenter providePresenter(MyPresenterImpl presenter) {
    // Dagger creates the object, we return it as a binding for the interface!
    return presenter;
  }
}

构造函数注入应该是使用Dagger的默认方式。确保您不要自己致电@Module interface MyModule { @Binds MyPresenter providePresenter(MyPresenterImpl presenter) } 或者您误解了这个概念。

现场注射

有些时候无法使用构造函数注入,例如Android中的Activity由Framework创建,您不应该覆盖构造函数。在这种情况下,我们可以使用字段注入

要使用字段注入,请注释要使用new初始化的所有字段,并向应处理注入的组件添加@Inject方法。

void inject(MyActivity activity)

在代码的某处,您必须致电@Component interface MyComponent { void inject(MyActivity activity); } ,否则字段将不会被初始化。例如在component.inject(myActivity)

onCreate(..)

字段注入不可传递。仅仅因为你注入了一个Activity,这并不意味着Dagger也会注入它注入的主持人的字段。您必须手动注入每个对象,这是您应该支持构造函数注入的一个原因。

有些工具可以帮助减轻创建组件和注入像void onCreate(..) { // fields still null / uninitialized myComponent.inject(this); // fields are now injected! // ... } 之类的对象的样板,这些对象将为您完成此任务,但仍需要完成。另一个例子是AppInjector,它添加了各种生命周期监听器来注入你的活动和片段,但它仍会调用AndroidInjection.inject()然后创建你的组件并注入对象。

确保在使用之前注入对象,并且没有使用AndroidInjection注释的构造函数以避免混淆。

还有什么?

还有较少使用的method injection,当然Dagger无法注入第三方库,您必须在模块中构建和提供这些库。

答案 1 :(得分:0)

上下文中删除 @Inject ,并创建一个单独的模块,以提供上下文依赖

@Module 
public class ContextModule {
    private final Context context;

    public ContextModule(Context context) {
        this.context = context;
    }

    @Provides
    @MyAppScope
    public Context getContext() {
        return context;
    }
}

然后创建你的DaggerComponent。 (我在Application类中创建了它,这个引用了 ApplicationContext

component =  DaggerDaggerAppComponent.builder()
            .contextModule(new ContextModule(this))
            .MyModule()
            .build();

如果需要,可以跳过 .MyModule(),因为与上下文模块不同,它没有外部依赖。