Страницы

Поиск по вопросам

Показаны сообщения с ярлыком dagger2. Показать все сообщения
Показаны сообщения с ярлыком dagger2. Показать все сообщения

воскресенье, 8 марта 2020 г.

Как правильно использовать Dagger2?

#java #android #dagger2


Нашел код и хочу разобраться как работает Dagger2

Вот я слепил такой простой пример и не могу понять как его соединить чтоб он работал

Есть MainActivity

public class MainActivity extends AppCompatActivity {

@Inject
protected ApiInterface apiInterface;

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

    if (apiInterface == null){
        Log.e("TAG", "apiInterface == null");
    }else {
        Log.e("TAG", "apiInterface != null : " + apiInterface.value);
    }
}
}


Я хочу инжекнуть в переменную apiInterface ссылку на класс. Для этого я указываю
ключевое слово @Inject.

Теперь, чтоб Dagger знал что инжекать туда я слепил вот такой класс

@Module
class ModelModule {

@Provides
@Singleton
ApiInterface provideApiInterface() {
    return ApiModule.getApiInterface();
}
}


Теперь я так понимаю Dagger когда увидит в MainActivity слово @Inject и тип переменной(насколько
я понимаю), то он пойдет искать кто ему возвращает такой тип и найдет класс ModelModule
в котором метод provideApiInterface() возвращает то что нужно apiInterface.

Вот кстати для ясности этот класс

class ApiInterface {
int value;

ApiInterface() {
    this.value = 4;
}
}


Но теперь, чтоб все заработало нужно указать @Component в котором указывается от
куда брать обьекты и куда внедрять... 

Но это теория, так как я не понимаю зачем это нужно... Мы и так указали уже от куда
брать обьекты, а словом @Inject мы указываем куда внедрять... Зачем еще что то нужно?

Вот тут и есть проблема, обьясните зачем нужен @Component и как его составить для
конкретного примера, чтоб все заработало и я мог в MainActivity получить число 4...

ПРАВКА
Вот я слепил вот такой компонент

@Singleton
@Component(modules = {ModelModule.class})
interface AppComponent {

void inject(ApiInterface apiInterface);
}


В MainActivity я все равно получаю null

Где ошибка?
    


Ответы

Ответ 1



Вам надо создать AppComponent в MainActivity appComponent = DaggerAppComponent.builder().modelModule(new ModelModule()).build(); и сделать appComponent.inject(this)

Ответ 2



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

четверг, 5 марта 2020 г.

Провайдинг зависимостей “перепрыгивая” через компонент

#java #android #dagger2


Имеется три пакета: a, b и c. Каждый из этих пакетов включает в себя интерфейс с
буквой, реализацию интерфейса, компонент, модуль и Scope (не знаю, как правильно переводится).
В результате, выходит вот такая структура проекта:

│   
│
├───a   A.java
│       AComponent.java
│       AImpl.java
│       AModule.java
│       AScope.java
│
├───b   B.java
│       BComponent.java
│       BImpl.java
│       BModule.java
│       BScope.java
│
└───c   C.java
        CComponent.java
        CImpl.java
        CModule.java
        CScope.java

Application.java


CComponent имеет зависимость (dependencies) на BComponent, а BComponent - на CComponent. 

BModule требует класс A для создания класса B, а CModule требует классы A и B для
создания класса C:

@AScope
@Component(modules = AModule.class)
public interface AComponent {
    A a();
}

@Module
public class AModule {
    @Provides
    @AScope
    public A provideA() {
        return new AImpl();
    }
}



@BScope
@Component(modules = BModule.class, dependencies = AComponent.class)
public interface BComponent {
    B b();
}

@Module
public class BModule {
    @Provides
    @BScope
    public B provideB(A a) {
        return new BImpl(a);
    }
}



@CScope
@Component(modules = CModule.class, dependencies = BComponent.class)
public interface CComponent {
    C c();
}


@Module
public class CModule {
    @Provides
    @CScope
    public C provideC(A a, B b) {
        return new CImpl(b, a);
    }
}




При попытке собрать проект, Dagger 2 начинает ругаться, что CComponent не может найти
провайдера класса A:

Error:(9, 7) java: com.dugin.rostislav.a.A cannot be provided without an @Provides-annotated
method.
      com.dugin.rostislav.a.A is injected at
          com.dugin.rostislav.c.CModule.provideC(a, …)
      com.dugin.rostislav.c.C is provided at
          com.dugin.rostislav.c.CComponent.c()




Почему компонент не может получить доступ к Provide-методам модулей компонентов,
который находятся ниже по иерархической цепочке зависимостей (более одного компонента)?
    


Ответы

Ответ 1



Проблема была в том, что компонент BComponent должен был явно указать компонентам, имеющим на него зависимости, какие реализации он может провайдить из нижестоящих компонентов. То есть он должен указать, что может дать класс A: @BScope @Component(modules = BModule.class, dependencies = AComponent.class) public interface BComponent { A a(); B b(); } Для того, чтобы пробрасывать зависимости неявно — можно использовать Subcomponent. Очень хорошая статья на хабре про Component Dependencies и Subcomponents, где раскрывается данный вопрос - Dagger 2. Часть вторая. Custom scopes, Component dependencies, Subcomponents.

понедельник, 6 января 2020 г.

error: cannot find symbol class DaggerAppComponent

#android #dagger2


Пишу проект с использование dagger2. Всё отлично работало, но, дописав новую фичу,
запускаю компилятор, а мне в ответ, что не может найти класс DaggerAppComponent, хотя
раньше всё работало. 

Application:

 @Override
public void onCreate() {
    super.onCreate();
    component = buildComponent();
}

private AppComponent buildComponent(){

    return DaggerAppComponent.builder()
            .appModule(new AppModule(this))
            .build();
}


Gradle(Module:app)

compile 'com.google.dagger:dagger:2.9'
annotationProcessor "com.google.dagger:dagger-compiler:2.9"

    


Ответы

Ответ 1



Т.к. класс DaggerAppComponent создаётся даггером на этапе компиляции, то любая ошибка компиляции не даст ему создасться. В этом случае надо смотреть в логи и смотреть какая у вас ошибка компиляции. В данном случае у вас не было дефолтного конструктора в одном из классов-моделей реалма.

воскресенье, 7 июля 2019 г.

Nulable аннотация показывает ошибку в Dagger2

У меня есть метод который может вернуть nullable обьект , я добавляю соответствующую аннотацию в метод и во все остальные методы которые используют этот обьект, но все равно получаю ошибку
Error:(17, 7) error: services.objects.response.CallServicePart is not nullable, but is being provided by @ui.dagger.dynamicFormComponent.PerDynamicForm @Provides @android.support.annotation.Nullable services.objects.response.CallServicePart ui.dagger.dynamicFormComponent.DynamicModule .getCallServicePart(models.AppModel) at: services.objects.response.CallServicePart is injected at ui.fragments.DynamicFormFragment.mServicePart ui.fragments.DynamicFormFragment is injected at ui.dagger.dynamicFormComponent.DynamicFormComponent.inject(iFragment)
Вот мой модуль
@Module public class DynamicModule {
...///
@PerDynamicForm @Provides @Nullable CallServicePart getCallServicePart(AppModel iAppModel) { return iAppModel.getServicePart(); }
@PerDynamicForm @Provides @Nullable ServicePartFormQuestionsManager getServicePartFormQuestionsManager(@CallNumber long iCallNumber, @Nullable CallServicePart iServicePart) { ServicePartFormQuestionsManager manager = null;
if (iServicePart != null) { manager = CallsManager.getInstance().getOpenCallData(iCallNumber).getServicePartFormQuestionManagerForPartCode(iServicePart.getPartCode()); }
return manager; }
.../// }
Что делаю не так?


Ответ

Аннотация @Nullable говорит о том, что возвращаемое значение параметра, поля или метода может быть .
If a {@code @Provides} method is marked {@code @Nullable}, Dagger will only allow injection into sites that are marked {@code @Nullable} as well. A component that attempts to pair a {@code @Nullable} provision with a non-{@code @Nullable} injection site will fail to compile.
Если метод отмечен как @Nullable, Dagger допускает инъекцию только на ссылки с отметкой @Nullable. Компонент, который пытается скомпилировать условие @Nullable с помощью команды non@Nullable, зафейлится.
Error:(17, 7) error: services.objects.response.CallServicePart is not nullable
Вот он и ругается, проверьте что класс имеет аннотации, на всякий случай сделайте clean project и rebuild.

понедельник, 17 июня 2019 г.

Gradle + Dagger2

Читаю tutorial по Dagger2, и возник вопрос, там сказано, что в root build.gradle нужно добавить:
dependencies { // other classpath definitions here classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' }
Где именно находится root build.gradle? Это build.bradle, который Project?


Ответ

Напишу решение к которому пришла, возможно кому-то еще тоже пригодится:
1) Можно добавить в root build.grade, в dependencies: classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' , даже не смотря на предупреждение, что туда нельзя добавлять.
2) Либо можно добавить в app/build.gradle такие строчки кода:
buildscript { repositories { jcenter() } dependencies { // the latest version of the android-apt plugin classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' } }
apply plugin: 'com.neenbedankt.android-apt'

Как избавиться от ошибки is bound multiple times в Dagger2?

Я понял так. Если тебе нужно передать параметры в метод который будет инжектнут, то нужно сделать еще один метод который будет предоставлять для Dagger этот параметр.
Вот что есть
@Module class ModelModule {
@Provides @Singleton ApiInterface provideApiInterface() { return ApiModule.getApiInterface(); } }
Метод который отлично работает. Теперь если нам нужно добавить в этот метод параметр, то нам сразу же нужно и создать метод который будет этот параметр предоставлять
вот так
@Module class ModelModule {
@Provides @Singleton int provideInt() { return 1; }
@Provides @Singleton ApiInterface provideApiInterface(int i) { return ApiModule.getApiInterface(i); } }
Но все дело в том, что у меня уже есть другой модуль в котором инициализирован метод с возвращающим типом int
@Module class AnotherModule { @Provides Integer getInt(){ return 3; } }
И получается что при запуске я получаю вот такую ошибку
Error:(11, 10) error: java.lang.Integer is bound multiple times: @Provides @Singleton int com.krokosha.aleksey.daggertwo.ModelModule.provideInt() @Provides Integer com.krokosha.aleksey.daggertwo.AnotherModule.getInt()
Получается я не могу использовать 2 метода которые возвращают одинаковый тип... Но как тогда быть?
Мне нужен он и там и там...


Ответ

В итоге я воспользовался Qualifier и получилось вот так
@Module class ModelModule {
@Provides @Named("FirstInt") int provideInt() { return 1; } }
@Module class AnotherModule {
@Provides @Named("SecondInt") int provideInt() { return 1; } }
@Inject protected ApiInterface apiInterface;
@Inject @Named("FirstInt") //or whatever you need protected Integer valueInt;

понедельник, 27 мая 2019 г.

Как правильно использовать Dagger2?

Нашел код и хочу разобраться как работает Dagger2
Вот я слепил такой простой пример и не могу понять как его соединить чтоб он работал
Есть MainActivity
public class MainActivity extends AppCompatActivity {
@Inject protected ApiInterface apiInterface;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
if (apiInterface == null){ Log.e("TAG", "apiInterface == null"); }else { Log.e("TAG", "apiInterface != null : " + apiInterface.value); } } }
Я хочу инжекнуть в переменную apiInterface ссылку на класс. Для этого я указываю ключевое слово @Inject
Теперь, чтоб Dagger знал что инжекать туда я слепил вот такой класс
@Module class ModelModule {
@Provides @Singleton ApiInterface provideApiInterface() { return ApiModule.getApiInterface(); } }
Теперь я так понимаю Dagger когда увидит в MainActivity слово @Inject и тип переменной(насколько я понимаю), то он пойдет искать кто ему возвращает такой тип и найдет класс ModelModule в котором метод provideApiInterface() возвращает то что нужно apiInterface
Вот кстати для ясности этот класс
class ApiInterface { int value;
ApiInterface() { this.value = 4; } }
Но теперь, чтоб все заработало нужно указать @Component в котором указывается от куда брать обьекты и куда внедрять...
Но это теория, так как я не понимаю зачем это нужно... Мы и так указали уже от куда брать обьекты, а словом @Inject мы указываем куда внедрять... Зачем еще что то нужно?
Вот тут и есть проблема, обьясните зачем нужен @Component и как его составить для конкретного примера, чтоб все заработало и я мог в MainActivity получить число 4...
ПРАВКА Вот я слепил вот такой компонент
@Singleton @Component(modules = {ModelModule.class}) interface AppComponent {
void inject(ApiInterface apiInterface); }
В MainActivity я все равно получаю null
Где ошибка?


Ответ

Вам надо создать AppComponent в MainActivity
appComponent = DaggerAppComponent.builder().modelModule(new ModelModule()).build();
и сделать appComponent.inject(this)

среда, 22 мая 2019 г.

Провайдинг зависимостей “перепрыгивая” через компонент

Имеется три пакета: a, b и c. Каждый из этих пакетов включает в себя интерфейс с буквой, реализацию интерфейса, компонент, модуль и Scope (не знаю, как правильно переводится). В результате, выходит вот такая структура проекта:
│ │ ├───a A.java │ AComponent.java │ AImpl.java │ AModule.java │ AScope.java │ ├───b B.java │ BComponent.java │ BImpl.java │ BModule.java │ BScope.java │ └───c C.java CComponent.java CImpl.java CModule.java CScope.java
Application.java
CComponent имеет зависимость (dependencies) на BComponent, а BComponent - на CComponent.
BModule требует класс A для создания класса B, а CModule требует классы A и B для создания класса C
@AScope @Component(modules = AModule.class) public interface AComponent { A a(); }
@Module public class AModule { @Provides @AScope public A provideA() { return new AImpl(); } }

@BScope @Component(modules = BModule.class, dependencies = AComponent.class) public interface BComponent { B b(); }
@Module public class BModule { @Provides @BScope public B provideB(A a) { return new BImpl(a); } }

@CScope @Component(modules = CModule.class, dependencies = BComponent.class) public interface CComponent { C c(); }
@Module public class CModule { @Provides @CScope public C provideC(A a, B b) { return new CImpl(b, a); } }

При попытке собрать проект, Dagger 2 начинает ругаться, что CComponent не может найти провайдера класса A
Error:(9, 7) java: com.dugin.rostislav.a.A cannot be provided without an @Provides-annotated method. com.dugin.rostislav.a.A is injected at com.dugin.rostislav.c.CModule.provideC(a, …) com.dugin.rostislav.c.C is provided at com.dugin.rostislav.c.CComponent.c()

Почему компонент не может получить доступ к Provide-методам модулей компонентов, который находятся ниже по иерархической цепочке зависимостей (более одного компонента)?


Ответ

Проблема была в том, что компонент BComponent должен был явно указать компонентам, имеющим на него зависимости, какие реализации он может провайдить из нижестоящих компонентов. То есть он должен указать, что может дать класс A
@BScope @Component(modules = BModule.class, dependencies = AComponent.class) public interface BComponent { A a(); B b(); }
Для того, чтобы пробрасывать зависимости неявно — можно использовать Subcomponent

Очень хорошая статья на хабре про Component Dependencies и Subcomponents, где раскрывается данный вопрос - Dagger 2. Часть вторая. Custom scopes, Component dependencies, Subcomponents