Dagger 2 Android不是注入领域

时间:2016-02-03 14:01:31

标签: android dagger-2

我正在尝试创建第一款Android Dagger应用。 我已经阅读了一些教程,现在我正在尝试使它工作。 问题是,当我在MainActivity中调用settings.get()时,我得到了

java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object javax.inject.Provider.get()' on a null object reference
    at biz.golek.whattodofordinner.MainActivity.onOptionsItemSelected(MainActivity.java:57)

是: 的 settings.get()执行命令();

我创建了:

MainActivity:

public class MainActivity extends AppCompatActivity {

    @Inject
    Provider<ShowSettingsController> settings;

    /.../

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            settings.get().Run();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

应用:

public class WhatToDoForDinnerApp extends com.orm.SugarApp {

    @Override
    public void onCreate() {
        super.onCreate();
        registerActivityLifecycleCallbacks(new ActivityInjector());
    }

}

ActivityInjector:

public class ActivityInjector implements Application.ActivityLifecycleCallbacks {
    private ApplicationComponent component;

    public ActivityInjector(){
        this.component = DaggerApplicationComponent.builder()
                .settingsModule(new SettingsModule())
                .build();
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState)       {
        component.inject(activity);
    }

    /.../
}

ApplicationComponent:

@Singleton
@Component(modules = { SettingsModule.class })
public interface ApplicationComponent {
    void inject(Activity activity);
}

和SettingsModule:

@Module
public class SettingsModule {

    @Provides
    @Singleton
    static ShowSettingsController provideShowSettingsController(){
        return new ShowSettingsControllerImpl();
    }

    /.../
}

整个代码在我的github:whattodofordinner Aby想法我错了什么? 提前谢谢。

4 个答案:

答案 0 :(得分:1)

我认为在您的ApplicationComponent中,您必须为每个活动定义inject - 方法。不可能使用组合物。

@Singleton
@Component(modules = { SettingsModule.class })
public interface ApplicationComponent {
   void inject(MainActivity activity);
   void inject(AnotherActivity activity);
}

编辑04.02.16:

此外,您的provide-method是静态声明的。尝试删除该静态。

@Module
public class SettingsModule {

   @Provides
   @Singleton
   ShowSettingsController provideShowSettingsController(){
      return new ShowSettingsControllerImpl();
   }

   /.../
}

您也可以在ApplicationComponent中声明一个provide语句:

@Singleton
@Component(modules = { SettingsModule.class })
public interface ApplicationComponent {

   SettingsController settingsController();

   void inject(MainActivity activity);
   void inject(AnotherActivity activity);
}

答案 1 :(得分:0)

您似乎错过了ApplicationComponent中的SettingsModule。如果您确实希望设置模块在整个应用程序的范围内可用,那么将其添加到模块列表中,如下所示:

@Singleton
@Component(modules = { AppModule.class, SettingsModule.class })
public interface ApplicationComponent {
    void inject(Activity activity);
}

你的ActivityInjector也必须像这样更新:

public class ActivityInjector implements Application.ActivityLifecycleCallbacks {
private ApplicationComponent component;

public ActivityInjector(){
    this.component = DaggerApplicationComponent.builder()
            .appModule(new AppModule())
            .settingsModule(new SettingsModule())
            .build();
}

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState)       {
    component.inject(activity);
}

/.../

答案 2 :(得分:0)

我已经完成了这项工作。不知道为什么,但Dagger不想按字段注入依赖项,但使用构造函数完美地工作。 IMO使用构造函数甚至更好(依赖关系通过构造函数参数显示),所以我已经改变了我的类以使用注入的构造函数。

第二件事是如何在不知道容器(组件)的情况下将依赖项放入Activity。

我是通过定义IAware接口来实现的:

public interface IAware<T> {
    void Set(T item);
}

然后标记接口IControllersProviderAware:

public interface IControllersProviderAware extends IAware<ControllersProvider> {
}

ControllersProvider类也不知道容器,它只知道javax.inject,因为使用了Provider类,但它是通用的,不依赖于Dagger(容器特定的实现):

public class ControllersProvider {
    private final Provider<ShowSettingsController> showSettingsControllerProvider;

    public ControllersProvider(Provider<ShowSettingsController> showSettingsControllerProvider)
    {
        this.showSettingsControllerProvider = showSettingsControllerProvider;
    }

    public ShowSettingsController getShowSettingsController() {
        return showSettingsControllerProvider.get();
    }
}

最后要做的是ControllersProviderInjector的实现,如果活动知道ControllersProvider,它负责将ControllersProvider设置为活动:

    public class ControllersProviderInjector implements Application.ActivityLifecycleCallbacks {

    private ControllersProvider controllersProvider;

    @Inject
    public ControllersProviderInjector(ControllersProvider controllersProvider) {
        this.controllersProvider = controllersProvider;
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        if (activity instanceof IControllersProviderAware)
            ((IControllersProviderAware)activity).Set(controllersProvider);
    }
}

Everythig在模块中注册:

@Module
public class AppModule {

    @Provides
    @Singleton
    static ControllersProvider provideControllersProvider(
            Provider<ShowSettingsController> showSettingsControllerProvider
    ){
        return new ControllersProvider(
            showSettingsControllerProvider
        );
    }
}

最后执行Activity:

public class MainActivity extends AppCompatActivity implements IControllersProviderAware {

    private ControllersProvider controllerProvider;

    /.../

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            this.controllerProvider.getShowSettingsController().Run();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public void Set(ControllersProvider item) {
        this.controllerProvider = item;
    }
}

执行此“并发症”而不是将getComponent方法添加到App,并从onCreate of Activity(如图所示here使用它)的主要优点是业务逻辑与容器细节的独立性。当有更好的容器而不是匕首时,为了改变它,我可以简单地改变我的ControllersProvider和容器特定的东西的实现。

一切都在我的github上:https://github.com/bartoszgolek/whattodofordinner

感谢大家的帮助,希望你能享受我的工作。

答案 3 :(得分:0)

我在github上查看了你的项目,并注意到使用dagger框架的一些误解。我做了几个修复和依赖注入很好。

首先,您需要使您的依赖图可用于其他应用程序组件。

public class WhatToDoForDinnerApp extends com.orm.SugarApp {
    private ApplicationComponent component;

    public ApplicationComponent getComponent() {
        return component;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        component = DaggerApplicationComponent.builder()
            .appModule(new AppModule())
            .settingsModule(new SettingsModule())
            .addNewDinnerModule(new AddNewDinnerModule())
            .build();
        ...
    }
}

然后你需要告诉匕首什么类期望注射。

@Singleton
@Component(modules = {AppModule.class, SettingsModule.class, AddNewDinnerModule.class})
public interface ApplicationComponent {
    ViewStateManager viewStateManager();
    ControllersProviderInjector controllersProviderInjector();

    void inject(MainActivity activity); //now you are able to inject dependencies in your MainActivity
}

最后你需要注入你需要的东西。在这种情况下,您可以注入AppModule中声明的所有模块(SettingsModuleAddNewDinnerModuleApplicationComponent)提供的所有项目。

public class MainActivity extends AppCompatActivity {

private View.OnClickListener listener;

@Inject
Provider<ShowSettingsController> showSettingsController;
@Inject
Provider<AddNewDinnerController> newDinnerController;


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

    ((WhatToDoForDinnerApp) getApplication()).getComponent().inject(this); //at this moment your dependencies are injected

    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab.setOnClickListener(...);

    listener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            newDinnerController.get().Run();
        }
    };
}
  @Override
public boolean onOptionsItemSelected(MenuItem item) {
    ...
    if (id == R.id.action_settings) {
        showSettingsController.get().Run();
        return true;
    }
    ...
}