Как я могу создать Android-библиотеку на основе Dagger, не заставляя потребляющие приложения использовать Dagger?

Я работаю над библиотекой Android, которая в основном является клиентом для некоторых служб REST, которые я написал. У меня есть несколько классов хранения, сетевых очередей, парсеров и т. Д., И, как и многие такие классы, они зависят от Context или таких вещей, как SharedPreferences, которые построены из Context. Все эти объекты скрыты за классом фасада, поэтому потребители моей библиотеки не видят их и не взаимодействуют с ними напрямую.

Для моего рассудка я хотел бы использовать Dagger 2 для внедрения зависимостей, чтобы управлять экземплярами этих классов ВНУТРЕННИЕ в моей библиотеке. Однако я не хочу заставлять приложения, использующие мою библиотеку, сами использовать Dagger; то, что я решил использовать Dagger, не означает, что мои пользователи должны это делать.

Все учебники, которые я видел, похоже, предполагают, что я создаю приложение, а не только библиотеку. Многие из этих руководств говорят мне, что я должен сделать свой Application класс унаследованным от DaggerApplication. Однако в моем случае у меня вообще нет Application (или каких-либо классов Activity или Service) в моей библиотеке, и я не хочу, чтобы мои пользователи использовали базовые классы Dagger.

Итак, как я могу использовать Dagger, не «утекая» его из моей библиотеки? Я нашел частичный ответ здесь, но я не уверен, как адаптировать "оболочку" автора "шаблон, чтобы справиться с моей зависимостью от Context. Могу ли я просто передать контекст в метод getComponent() оболочки, или Dagger сможет получить ссылку на контекст другим способом?


person Dalbergia    schedule 10.07.2017    source источник


Ответы (1)


Библиотека почти похожа на приложение (когда дело доходит до Dagger). Да, у вас нет application объекта, но он вам и не нужен.

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

Разрешите пользователям передавать Context, когда они впервые обращаются к вашей библиотеке (например). Имейте DaggerInjector (я думаю, ваш образец называет его оболочкой), который имеет статическую ссылку на ваш Component интерфейс.

Пример (и как таковой, просто общий пример):

public class DaggerInjector {

    private static YourComponent component;

    private DaggerInjector() {
        super();
    }

    public static YourComponent getComponent() {
        return component;
    }

    public static YourComponent buildComponent(Context context) {
        component = DaggerYourComponent
                .builder()
                .yourModule(new YourModule(context))
                .build();
        return component;
    }
}

Ваш «модуль» может выглядеть так:

@Module
public class YourModule {

    private Context context;

    public YourModule(Context context) {
        this.context = context;
    }

    @Provides
    @Singleton
    final Context providesContext() {
        return context;
    }
}

Чтобы использовать это:

Пусть ваши пользователи вызывают метод (или вы вызываете его в первый раз, если компонент равен нулю):

DaggerInjector.buildComponent(context);

Это обеспечит инициализацию компонента Dagger и генерацию кода. Поймите, что вызов buildComponent - дорогостоящая задача (Dagger должен делать много!), Поэтому делайте это только один раз (если вам не нужно повторно инициализировать библиотеку с другими значениями, известными только во время выполнения).

Некоторые библиотеки просто запрашивают контекст при каждом вызове, так что это не исключено; вы могли бы затем инициализировать dagger при первом вызове (проверив, имеет ли getComponent () значение null в инжекторе).

После того, как ваш DaggerInjector.getComponent() больше не равен нулю, теперь вы можете добавить @Inject и соответствующий «вводимый» материал…

например: в YourModule вы могли бы иметь:

@Provides
SomeObject providesSomeObject() {
    return new SomeObject();
}

// THIS “Context” here is automatically injected by Dagger thanks to the above.
@Provides
@Singleton
SomeOtherObject providesSomeOtherObject(Context context) {
    return new SomeOtherObject(context); //assume this one needs it
}

и в любом «вводимом» объекте (то есть в объекте, имеющем inject метод в вашем компоненте…) вы можете:

public class AnObjectThatWantsToInjectStuff {

    @Inject
    SomeObject someObject;
    @Inject 
    SomeOtherObject someOtherObject;

    public AnObjectThatWantsToInjectStuff() {
          super();
          DaggerInjector.getComponent().inject(this);

          // you can now use someObject and someOtherObject
    }
}

Чтобы вышеперечисленное работало, вам нужен код YourComponent (который является интерфейсом), например:

void inject(AnObjectThatWantsToInjectStuff object);

(в противном случае вызов DaggerInjector.getComponent().inject(this) завершится ошибкой во время компиляции)

Заметьте, я никогда не передавал контекст YourInjectableContext, Dagger уже знает, как его получить.

Будьте осторожны с утечками. Я рекомендую вам хранить context.getApplicationContext(), а не просто Context для всех / большинства случаев (если вам явно не нужен контекст Activity для надувания макетов / целей, контекст приложения, предоставляемый приложением-потребителем, - это все, что вам нужно).

person Martin Marconcini    schedule 10.07.2017
comment
Большое вам спасибо за этот ответ! Я последовал вашим предложениям, и все работает хорошо. - person Dalbergia; 11.07.2017
comment
У кого-нибудь работает? Выполнял тот же процесс, но не работал. Я получаю ошибку «не могу найти класс символа DaggerYourComponent» при создании приложения. @Dalbergia, А без UI компонента андроида работает? - person Tejas Mehta; 17.07.2017
comment
Это работает для меня, и, по-видимому, OP также заставил его работать. @TejasMehta попробуйте очистить / восстановить или что-то в этом роде. Ваша ошибка означает, что ваш компонент был неправильно сгенерирован или вы используете неправильное имя. - person Martin Marconcini; 17.07.2017
comment
Спасибо @MartinMarconcini После многократной очистки / восстановления проблема была решена. Теперь работает. - person Tejas Mehta; 18.07.2017
comment
@TejasMehta, да, ответ Мартина Маркончини был правильным, как вы обнаружили. DaggerYourComponent - это реализация интерфейса YourComponent, сгенерированного Dagger как часть процесса сборки, поэтому вы должны принудительно выполнить сборку до того, как класс будет распознан. - person Dalbergia; 18.07.2017
comment
Как это работает с DaggerAppCompatActivity? Я получаю ошибки, связанные с форсунками. - person masterwok; 19.06.2018
comment
Как этого добиться, если я создал компонент, а моя библиотека использует разные модули ..? - person Himanshu; 28.05.2020
comment
@Himanshu Я не совсем понимаю, что вы имеете в виду под использует разные модули, Dagger - сложная программа, она может многое делать и может сбивать с толку; Я предлагаю вам задать конкретный вопрос, чтобы получить лучший ответ. - person Martin Marconcini; 28.05.2020
comment
@VikashParajuli Конечно, вот официальная документация Google об использовании Android / Hilt. . Очень легко следить, и вы должны будете запустить его через 10 минут. - person Martin Marconcini; 15.05.2021
comment
@MartinMarconcini Моя опечатка. Я хочу создать библиотеку Android без Activity и Fragment. Не могли бы вы помочь мне с этим? - person Vikash Parajuli; 16.05.2021
comment
@VikashParajuli У меня не было необходимости делать или пробовать это. Так что я не могу вам с этим помочь. Однако вы можете искать и находить других людей, пытающихся сделать то же самое. Возможно, вы найдете там свой ответ. - person Martin Marconcini; 16.05.2021