Кама и вложени инжекции

Използвам Dagger за инжектиране на зависимости в приложение за Android и се натъкнах на проблем, който съм не съм напълно сигурен как да разреша по чист начин.

Това, което се опитвам да постигна, е да създам помощници и да ги инжектирам в моята дейност, като тези помощници също съдържат инжектирани членове.

Какво работи

Дейността, при която моят помощник се инжектира:

public class MyActivity extends Activity {
    @Inject SampleHelper helper;

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

        ((MyApplication) getApplication()).inject(this);

        Log.i("debug", "helper = " + helper);
        Log.i("debug", "helper context = " + helper.context);
    }
}

Приложението, създаващо обектната графика:

public class MyApplication extends Application {
    private ObjectGraph graph;

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

        graph = ObjectGraph.create(getModules());
    }

    private Object[] getModules() {
        return new Object[] { new MyModule(this) };
    }

    public void inject(Object target) {
        graph.inject(target);
    }
}

Инжектирането работи перфектно, когато инстанцирам директно клас SampleHelper, който от своя страна получава инжектиран контекст на приложение:

@Singleton
public class SampleHelper {

    @Inject public Context context;

    @Inject
    public SampleHelper() {}
}

Със следния модул:

@Module(
    injects = { MyActivity.class },
    complete = false,
    library = true
)
public class MyModule {
    private final MyApplication application;

    public MyModule(MyApplication application) {
      this.application = application;
    }

    @Provides @Singleton Context provideApplicationContext() {
        return application;
    }
}

Какво не работи

Въпреки това, когато отделя помощния интерфейс от неговата реализация:

public interface SampleHelper {
}

@Singleton
public class SampleHelperImpl implements SampleHelper {

    @Inject public Context context;

    @Inject
    public SampleHelperImpl() {}
}

И добавете това към модула кама:

public class MyModule {
    ...

    // added this method
    @Provides @Singleton public SampleHelper provideSampleHelper() {
        return new SampleHelperImpl();
    }

    ...
}

Контекстът не се инжектира в моя SampleHelperImpl, както бих очаквал. Предполагам, че това се дължи на това, че SampleHelperImpl се инстанцира чрез директно извикване на конструктор, а не чрез извикване на иницииран от инжектиране конструктор, защото MyModule#provideApplicationContext() дори не се извиква, така че предполагам, че пропускам нещо за Dagger (което е вероятно, тъй като моите предишни DI преживявания включват само пролет).

Някаква идея за това как да инжектирам моя контекст в моята инжектирана помощна реализация по начин "чист кама"?

Благодаря много!


person mrlem    schedule 15.07.2013    source източник
comment
Аз също се боря с камата в момента и имам подобна конфигурация на твоята. Трябва да инжектирам екземпляри на дейности в помощници, а не самото приложение, но иначе е много подобно. Ето защо имам въпрос към вас: защо инжектирате контекст като член в SampleHelper, а не като параметър на конструктор? В моя случай инжектирането чрез параметър на конструктора е неуспешно и затова се чудя дали има нещо специално за него, което трябва да се избягва. Разбирам, че не е по темата, но ако можете да помогнете, ще бъдем благодарни.   -  person Haspemulator    schedule 24.07.2013
comment
Всъщност сега предавам контекста като параметър на конструктор (както е споменато в отговора ми по-долу), въпреки че не се инжектира директно в самия конструктор, а по-скоро като част от метод @Provides в модула на камата (и работи). Първоначалната ми идея беше да премахна целия код, свързан с присвояването на членовете ми благодарение на dagger, но не успях да го накарам да бъде толкова лесен за използване, колкото, да речем, пружинно впръскване. Като се има предвид това, аз съм наясно, че те не работят по същия начин и със същите ограничения, а камата все още носи много.   -  person mrlem    schedule 24.07.2013
comment
Здравейте, благодаря за отговора. Може би можете да погледнете въпроса ми? stackoverflow.com/questions/17839451/   -  person Haspemulator    schedule 24.07.2013


Отговори (4)


Това е доста стар въпрос, но мисля, че това, което искате, е следното:

@Provides @Singleton public SampleHelper provideSampleHelper(SampleHelperImpl impl) {
    return impl;
}

По този начин Dagger ще създаде вашето SampleHelperImpl и следователно ще го инжектира.

person alexanderblom    schedule 27.08.2013
comment
Не е твърде късно: наистина отговаря перфектно на въпроса ми. Благодаря много! - person mrlem; 28.08.2013
comment
@alexanderblom Бихте ли обяснили разликата между това, което направихте вие ​​и това, което направи mrlem? - person peacepassion; 14.09.2014

В случай, че някой се интересува, когато имплементирате метод @Provides в модул на кама, можете да получите екземпляри на обработени с кама обекти като този:

@Provides @Singleton public SampleHelper provideSampleHelper(Context context) {
    SampleHelper helper = new SampleHelperImpl();
    helper.setContext(context);
    return helper;
}

Това работи, но все още го чувствам малко тромаво, тъй като трябва изрично да наричам моите помощници сетери (обикновено това, от което искате да се отървете с инжектиране).

(Ще изчакам малко, в случай че някой дойде с по-добро решение)

person mrlem    schedule 16.07.2013

(Отнася се за Dagger v1.0.1)

Уверете се, че използвате адаптер за инжектиране. С отразяващо инжектиране, dagger очевидно не прави транзитивно инжектиране на @Provides обекти. Мисля, че това е грешка.

person André W8    schedule 23.07.2013
comment
Това звучи като мой проблем. Но можете ли да изясните 2 неща: 1/ Мислех, че не използва отражение за своите инжекции? (което предполагам имахте предвид под отразяващо инжектиране или имахте предвид инжектиране, базирано на анотации?) 2/ какво имате предвид под инжектиране на адаптер: инжекции, използващи @Provides в модула на камата? - person mrlem; 24.07.2013
comment
Dagger предоставя два начина за задаване на @Inject пояснени полета. Единият е чрез отражение, другият е чрез генериран клас адаптер. И в двата случая полетата трябва да са поне пакетно достъпни. Методът на отразяване е резервен и може да не е наличен в бъдещи версии. За да проверите какъв метод на инжектиране използвате, задайте точка на прекъсване в @Provides метода на вашия модул и прегледайте трасирането на стека. Внимавайте за: injectMembers():118, ReflectiveAtInjectBinding {dagger.internal.plugins.reflect} срещу injectMembers():82, YourClassname$$InjectAdapter {your.classes.package} - person André W8; 31.07.2013

По отношение на инжектирането на правилния контекст, може да искате да погледнете този пример https://github.com/square/dagger/tree/master/examples/android-activity-graphs.

person jfrey    schedule 16.07.2013
comment
Благодаря за отговора: Вече попаднах на този пример (няма много от тях ;). Наистина, в приложение от реалния живот трябва да инжектирате правилния контекст. Но първоначалният ми въпрос беше малко по-общ: разделянето на помощния интерфейс от имплементацията не позволява на @Inject да работи в рамките на имплементацията, тъй като инстанцията на помощника вече не се обработва директно от Dagger. - person mrlem; 17.07.2013