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

Dav*_*jak 4 android dagger-2

这是一个典型问题,因为对Dagger 2的对象初始化存在很多误解.

如果您的问题被标记为重复,请仔细阅读本文,并确保了解构造函数注入和现场注入之间的区别.

我尝试向Context我的演示者注入一个,但在尝试使用它时我得到一个NullPointerException.

class MyPresenter {

  @Inject Context context;

  private MyView view;

  @Inject
  MyPresenter(MyView view) {
    this.view = view;
  }
}
Run Code Online (Sandbox Code Playgroud)

我的模块看起来像这样

@Module
class MyModule {

  @Provides
  MyPresenter provideMyPresenter(MyView view) {
    return new MyPresenter(view);
  }
}
Run Code Online (Sandbox Code Playgroud)

我在这里的活动中注入了演示者:

class MyActivity extends Activity {

  @Inject MyPresenter presenter;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    createMyActivityComponent().inject(this);
  }
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*jak 7

以上包括构造函数和字段注入,但都没有做对.如果我们删除了所有@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
Run Code Online (Sandbox Code Playgroud)

确保使用两种构造器注入字段注入.混合两者通常表示您的设置或理解错误.

  • @Inject在场上是场注入的标记
  • @Inject在构造函数上是构造函数注入的标记

这意味着你的类应该有任何的

  • 一个单一 @Inject的构造,或
  • @Inject的所有字段进行初始化,但没有在构造!

不要@Inject到处洒,希望事情有效!确保将注释放在需要的位置.不要混合场和构造函数注入!

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

构造函数注入

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

创建对象的Dagger也意味着模块不需要@Provides创建对象的方法.您需要做的就是添加@Inject到构造函数并声明依赖项.

class MyPresenter {

  private Context context;
  private MyView view;

  @Inject
  MyPresenter(MyView view, Context context) {
    this.view = view;
    this.context = context
  }
}
Run Code Online (Sandbox Code Playgroud)

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

@Module class MyModule {

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

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

@Module interface MyModule {

  @Binds
  MyPresenter providePresenter(MyPresenterImpl presenter)
}
Run Code Online (Sandbox Code Playgroud)

构造函数注入应该是使用Dagger的默认方式.确保你没有给new自己打电话或者你误解了这个概念.

现场注射

有些时候你不能使用构造函数注入,例如,Android中的Activity由Framework创建,你不应该覆盖构造函数.在这种情况下,我们可以使用现场注入.

要使用字段注入,请注释要初始化的所有字段,并向应处理注入的组件@Inject添加void inject(MyActivity activity)方法.

@Component
interface MyComponent {
  void inject(MyActivity activity);
}
Run Code Online (Sandbox Code Playgroud)

在代码中的某个地方,您必须调用component.inject(myActivity) 或者字段不会被初始化.例如inonCreate(..)

void onCreate(..) {
  // fields still null / uninitialized
  myComponent.inject(this);
  // fields are now injected!

  // ...
}
Run Code Online (Sandbox Code Playgroud)

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

有些工具可以帮助减轻创建组件和注入对象的样板,AndroidInjection.inject()这样就可以为您完成此任务,但仍有必要完成.另一个例子是AppInjector添加各种生命周期监听器来注入你的活动和片段,但它仍然会调用AndroidInjection然后创建你的组件并注入对象.

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

还有什么?

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

  • 您说使用构造函数注入时,带注释的字段将被忽略。但这并不完全正确,对吧?如果您使用“@Inject”注释字段或方法,同时还有一个“@Inject”注释的构造函数,它将在初始化后注入该字段/方法 (2认同)