Понимание подкомпонентов Dagger2

У меня есть следующая архитектура Dagger2 в моем приложении:

-- AppComponent (@PerApplication)
  -- UserComponent (@PerUser)
    -- ActivityComponent (@PerActivity)
      -- ChatComponent (@PerActivity) <-- 1

Где: AppComponent:

@PerApplication
@Component(modules = {ApplicationModule.class, StorageModule.class, NetworkModule.class})
public interface ApplicationComponent {
    UserComponent plus(UserModule userComponent);

    //Exposed to sub-graphs.
    Context application();
}

Пользовательский компонент:

@PerUser
@Subcomponent(modules = {UserModule.class, RosterModule.class})
public interface UserComponent {
    ActivityComponent plus(ActivityModule activityModule);

    User getMe();

    UserRepository userRepository();
}

Компонент Активности:

@PerActivity
@Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {

    ChatComponent plus(ChatModule chatComponent);

    //Exposed to sub-graphs.
    Context context();
}

ЧатКомпонент:

@PerActivity
@Subcomponent(modules = {ChatModule.class})
public interface ChatComponent {
    void inject(ChatListFragment chatListFragment);
    void inject(ConversationFragment conversationFragment);
    void inject(NewConversationFragment newConversationFragment);
    void inject(CloudFilesFragment cloudFilesFragment);
    void inject(ChatActivity chatActivity);
    void inject(ConversationActivity conversationActivity);
    void inject(NewConversationActivity newConversationActivity);

    void inject(NewGroupActivity newGroupActivity);
    void inject(NewGroupFragment newGroupFragment);
}

Я столкнулся с 2 проблемами:

Во-первых, как я могу внедрить разные Context в свои классы? Либо приложение, либо активность ??

И во-вторых, я столкнулся со странной проблемой при попытке скомпилировать свой код, ошибка:

Ошибка: (23, 10) ошибка: br.com.animaeducacao.ulife.domain.interactor.UseCase не может быть предоставлен без аннотированного метода @Provides. br.com.animaeducacao.ulife.presentation.view.fragment.ChatListFragment.chatListPresenter [введенное поле типа: br.com.animaeducacao.ulife.presentation.presenter.ChatListPresenter chatListPresenter] br.com.animaeducacao.ulife.presentation.presenter. ChatListPresenter.(br.com.animaeducacao.ulife.domain.interactor.UseCase chatDialogsUseCase, br.com.animaeducacao.ulife.domain.interactor.UseCase советаUserPresence, android.content.Context контекст) [параметр: @javax.inject.Named( "getChatDialogs") br.com.animaeducacao.ulife.domain.interactor.UseCase chatDialogsUseCase]

Мой ChatListFragment:

@PerActivity
public class ChatListFragment extends BaseFragment implements ChatListView {

    @Inject
    ChatListPresenter chatListPresenter;
  ...
//called onActivityCreated()
private void initialize() {
        this.getComponent(ChatComponent.class).inject(this);
}

Базовый фрагмент:

protected <C> C getComponent(Class<C> componentType) {
    return componentType.cast(((HasComponent<C>)getActivity()).getComponent());
  }

Ведущий списка чатов:

@PerActivity
public class ChatListPresenter implements Presenter {

    private final UseCase chatDialogsUseCase;
    private final UseCase adviceUserPresence;
    private final Context context;
    private ChatListView chatListView;

    @Inject
    public ChatListPresenter(@Named("getChatDialogs") UseCase chatDialogsUseCase,
                             @Named("adviceUserPresence") UseCase adviceUserPresence,
                             Context context) {
        this.chatDialogsUseCase = chatDialogsUseCase;
        this.adviceUserPresence = adviceUserPresence;
        this.context = context;
    }

Проблема в том, что в моем классе ChatModule я реализовал все необходимые @Provides:

@Provides
    @PerActivity
    @Named("getChatDialogs")
    public UseCase provideChatDialogs(@Named("transactionalChatRepository") ChatRepository chatRepository, ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread) {
        return new GetUserChatDialogs(chatRepository, threadExecutor, postExecutionThread);
    }

Хороший ли это подход? Почему это не компилируется, чего мне здесь не хватает? Извините за длинный пост и спасибо!


person Leonardo    schedule 31.12.2015    source источник
comment
Пожалуйста, прочитайте еще несколько руководств по кинжалу. Ошибка, которую вы получаете, вероятно, связана с тем, что вы предоставляете только именованный вариант использования. Кроме того, вы не должны заботиться о context. Если вам нужна активность, введите активность, если вам нужен контекст, вам, вероятно, следует перейти к контексту приложения.   -  person David Medenjak    schedule 31.12.2015


Ответы (1)


Ах, у вас есть несколько проблем.

1.) Хотя вы правильно используете подобласть до определенного момента (вы сначала правильно создаете @Subcomponent), ChatComponent на самом деле не подпадает под область своего родительского компонента - в основном, ChatComponent не может быть @PerActivity, это должна быть четвертая сфера.

Аннотация @Subcomponent — это просто способ создать компонент с подобластью без необходимости указывать его как зависимость компонента. Ему по-прежнему нужна своя «более конкретная» область применения.

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

Например, ваш ApplicationComponent не имеет методов обеспечения того, что находится в StorageModule, и поэтому зависимости, предоставляемые StorageModule, не могут быть унаследованы компонентами с подобластями.

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

Кроме того, чтобы в иерархии областей видимости A->B->C C мог наследовать от A, тогда B также должен иметь методы обеспечения A.

Значит надо UserComponent extends ApplicationComponent, и ActivityComponent extends UserComponent, и ChatComponent extends ActivityComponent.

3.) Вы должны использовать аннотации @Named("application") и @Named("activity"), чтобы указать два разных Context, или вместо этого просто ссылаться на них как Application и Activity в вашем модуле, чтобы они не перепутались.

person EpicPandaForce    schedule 31.12.2015
comment
Цитирование для Dagger2 Javadoc: подкомпонент, который наследует привязки от родительского {@link Component} или * {@link Subcomponent}. Я не понимаю, почему я должен расширять любой интерфейс, используя @Subcomponent. Я пробовал, но при сборке получил ошибку Stackoverflow. Спасибо за ответ и объяснения до сих пор! - person Leonardo; 31.12.2015
comment
Ну, технически я знаю, что это требуется для зависимостей компонентов, но я не уверен, что это нужно и подкомпонентам. Вы изменили область действия компонента чата? - person EpicPandaForce; 31.12.2015
comment
В этом случае вам нужно будет опубликовать код своего модуля... Но я думаю, что помогло бы, если бы у вас были классы для каждого варианта использования, поэтому вам не нужно было бы использовать @Named для них, возможно, вы забыли один из них. - person EpicPandaForce; 02.01.2016