Как взаимно связать фрагменты с докладчиками, используя dagger-android?

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

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

Вот базовый код, который я использую для действий:

Основной вид деятельности

public interface MainActivityContract {
    interface View {
        void setText(String text);
    }

    interface Presenter {
        void attach();
    }
}

Основной модуль деятельности

@Module
public abstract class MainActivityModule {

    @Binds
    abstract MainActivityContract.View exampleFragment(MainActivity view);

    @Binds
    abstract MainActivityContract.Presenter exampleFragmentPresenter(MainActivityPresenter presenter);
}

Код деятельности:

public class MainActivity extends DaggerAppCompatActivity implements MainActivityContract.View {
    @Inject
    MainActivityContract.Presenter mPresenter;

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

        mPresenter.attach();
    }

    @Override
    public void setText(String text) {
        ((TextView) findViewById(R.id.example_text)).setText(text);
    }
}

Код докладчика:

public class MainActivityPresenter implements MainActivityContract.Presenter {

    private MainActivityContract.View mView;

    @Inject
    public MainActivityPresenter(MainActivityContract.View view) {
        mView = view;
    }

    public void attach() {
        this.mView.setText("Hello world!");
    }
}

Обычная установка dagger-android:

Код приложения

public class MyApplication extends DaggerApplication {
    @Override
    protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
        return DaggerAppComponent.builder().application(this).build();
    }
}

AppComponent

@Component(modules = {ActivityBindingModule.class, AndroidSupportInjectionModule.class})
public interface AppComponent extends AndroidInjector<MyApplication> {
    @Component.Builder
    interface Builder {
        @BindsInstance
        AppComponent.Builder application(Application application);

        AppComponent build();
    }
}

Модуль привязки активности

@Module
public abstract class ActivityBindingModule {
    @ActivityScoped
    @ContributesAndroidInjector(modules = MainActivityModule.class)
    abstract MainActivity mainActivity();
}

Теперь я хочу преобразовать активность в фрагмент.

Вышеприведенный шаблон повторяется с единственным отличием во фрагменте кода:

public class ExampleFragment extends DaggerFragment implements ExampleFragmentContract.View {
    @Inject
    ExampleFragmentContract.Presenter mPresenter;

    @Inject
    public ExampleFragment() {

    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.example_fragment, container, false);
    }

    @Override
    public void setText(String text) {
        ((TextView) getView().findViewById(R.id.example_text)).setText(text);
    }
}

Основная активность отключена, а докладчик удален:

public class MainActivity extends DaggerAppCompatActivity {
    @Inject
    ExampleFragment exampleFragment;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

        fragmentTransaction.add(R.id.root, exampleFragment);
    }
}

Модуль остается, но теперь вставляет фрагмент:

@Module
public abstract class MainActivityModule {
    @FragmentScoped
    @ContributesAndroidInjector(modules = ExampleFragmentModule.class)
    abstract ExampleFragment exampleFragment();
}

Компиляция теперь завершается с ошибкой:

Error:(13, 8) error: [dagger.android.AndroidInjector.inject(T)] com.example.michael.daggerfragmentexample.ui.ExampleFragment.ExampleFragmentContract.Presenter cannot be provided without an @Provides-annotated method.
com.example.michael.daggerfragmentexample.ui.ExampleFragment.ExampleFragmentContract.Presenter is injected at
com.example.michael.daggerfragmentexample.ui.ExampleFragment.ExampleFragment.mPresenter
com.example.michael.daggerfragmentexample.ui.ExampleFragment.ExampleFragment is injected at
com.example.michael.daggerfragmentexample.ui.MainActivity.MainActivity.exampleFragment
com.example.michael.daggerfragmentexample.ui.MainActivity.MainActivity is injected at
dagger.android.AndroidInjector.inject(arg0)
A binding with matching key exists in component: com.example.michael.daggerfragmentexample.ui.MainActivity.MainActivityModule_ExampleFragment.ExampleFragmentSubcomponent

На мой взгляд, у меня есть два уровня глубоких подкомпонентов, и кинжал игнорирует более глубокий модуль. Это то, что я не могу сделать?

Ссылка на полный образец кода.

1 ответ

Решение

Из вашего ExampleFragmentPresenter вам не хватает области видимости

@FragmentScoped
public class ExampleFragmentPresenter implements ExampleFragmentContract.Presenter

Кроме того, вы пытаетесь добавить инъекцию области фрагмента в область действия из MainActivity.

@Inject
ExampleFragment exampleFragment;

Это неправильно, так как ваша область действия ничего не знает о введении фрагмента, поэтому дополнительно сделайте это:

public class MainActivity extends DaggerAppCompatActivity {

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

        fragmentTransaction.add(R.id.root, ExampleFragment.newInstance());
    }
}

// removed @Inject here
public ExampleFragment() {

}
Другие вопросы по тегам