2016-02-03 2 views
1

Я пытаюсь создать первое приложение для Android с кинжалом. Я прочитал несколько уроков, и теперь я пытаюсь заставить его работать. Проблема заключается в том, когда я называю settings.get() в MainActivity, я получаюDagger 2 Android не вводит поле

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() Run();.

Я создал:

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

App:

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 идеи ПБЯ, что я неправильно? Спасибо заранее.

+0

Я просто изучаю кинжал 2, но, думаю, вам нужен компонент для вашего модуля настроек. Этот урок помог мне: https://blog.gouline.net/2015/05/04/dagger-2-even-sharper-less-square/ – dazza5000

ответ

0

Кажется, вам не хватает параметра SettingsModule в вашем ApplicationComponent. Если вы хотите, чтобы модуль настройки будет доступен в рамках всего приложения затем добавить его в список модулей, как это:

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

/.../ 
+0

Я удалил код перед публикацией, и, похоже, я удаляю модуль по ошибке. Я исправил это под вопросом. –

+0

Возможно, вам следует пояснить, что вы отредактировали свой вопрос с правильным ответом, иначе это может смутить любого, кто приходит. –

+1

Провайдер должен избегать ссылки на целую жизнь приложения и ленивую инициализацию. Когда я прочитал его часть Кинжала. –

1

Я думаю, что в вашем ApplicationComponent вы должны определить inject -Метод для каждой деятельности. Использовать композицию невозможно.

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

Edit 04.02.16:

Кроме того, ваш обеспечивает-метод статического объявлен. Попытайтесь удалить этот статический файл.

@Module 
public class SettingsModule { 

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

    /.../ 
} 

Вы также можете объявить предоставляет заявление в вашем ApplicationComponent:

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

    SettingsController settingsController(); 

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

Я проверил. Все еще не работает. Я добавил только, чтобы проверить приведение в MainActivity в ActivityInjector.onActivityCreated и изменить подпись, если вы введете MainActivity. –

+0

Вы пытались очистить и перестроить свой проект? Извините, у меня нет дальнейшей идеи. – Christopher

+0

Да, я сделал. Не помогло. –

0

Я finnaly получил эту работу. Не знаю, почему, но Dagger не хочет вводить зависимости по полю, но отлично работает с использованием конструктора. Использование конструктора ИМО еще лучше (зависимости показаны явным по параметрам конструктора), поэтому я изменил свои классы на использование инжектируемого конструктора.

Во-вторых, как установить зависимости для Activity, не сообщая ему о контейнере (компоненте).

Я сделал это путем определения интерфейса IAware:

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

А затем интерфейс маркера IControllersProviderAware:

public interface IControllersProviderAware extends IAware<ControllersProvider> { 
} 

Класс ControllersProvider также не знает о контейнере, оно известно только javax.inject , из-за использования класса Provider, но он является общим и не зависит от кинжала (конкретная реализация контейнера):

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

и, наконец, осуществление деятельности:

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 в приложение и использовать его из onCreate of Activity, как показано на рисунке here, является независимостью бизнес-логики от специфики контейнера. Когда будет какой-либо лучший контейнер, чем кинжал, чтобы изменить его, я могу просто изменить свою реализацию ControllersProvider и конкретных контейнеров.

Все, конечно, на моем GitHub: https://github.com/bartoszgolek/whattodofordinner

Спасибо за помощь для всех и надеюсь, что вы будете наслаждаться моей работой.

0

Я посмотрел на ваш проект на github и заметил некоторое недоразумение в использовании рамки кинжала. Я сделал несколько исправлений и зависимостей, введенных штрафом.

Прежде всего, вам нужно сделать свой график зависимостей доступным для других компонентов приложения.

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, SettingsModule, AddNewDinnerModule), объявленный в ApplicationComponent.

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; 
    } 
    ... 
} 
+0

Большое спасибо, Что меня беспокоит в этом решении, все activiteis знают о компоненте. Компонент - это кинжал. Он крепко связывает меня с кинжалом. Вторая вещь - это метод требований для каждого вида деятельности, который я хочу ввести. Как я писал в своем ответе, я нашел решение, свободное от этой неудобства. У него все еще есть некоторые неудобства, такие как добавление метода для каждого контроллера к контроллеруProvider, но контроллеры - это точки входа в логику бизнес-процессов, поэтому IMO это менее вредно. –

+0

yeap Я получил его, я имею в виду, что ваш код выглядит так, как будто ему не нужен кинжал, но в случае, если вы хотели бы использовать его, вы, вероятно, должны больше узнать о построении графика зависимости с кинжалом, например, есть большой учебник (источники с несколькими статьями): https://github.com/frogermcs/GithubClient – greenfrvr

+0

Это не например, «это не настоящая необходимость», его больше похоже на «использование чего-то, но не на привязку». В случае, если, может быть, через какое-то время будет более четкое решение, чем Dagger2, или это будет Dagger3 И я не хочу менять всю свою деятельность. Снова thaks для вашей помощи. –

Смежные вопросы