Dagger 2 학습 및 탐색(5)

8401 단어
지난 호에서는 대상 코드를 바꾸지 않는 주입 방식을 설명했는데 사실은 구성원 주입(field injection)이었다.Dagger는 구조기 주입(constructor injection), 메소드 주입(method injection)과 구성원 주입 등 3가지 주입 방식을 제공합니다.이전에 대상 구조기 코드에 @Inject를 추가한 것은 구조기 주입에 속했고 방법 주입도 간단했다. 일반적으로 get 방법으로 필요한 것을 얻는다. 방법 주입이라고 해도 구성원 주입은 구성원 변수에 @Inject 기호를 추가하고 Dagger는 값을 부여함으로써 주입을 실현한다.

바디 코드


그럼 이제 더 복잡한 교차 의존 관계를 탐색해 보자.현재 ClassB:
public class ClassB {
  private ClassA classA;
  private int a;

  public ClassB(ClassA classA, int a) {
    this.classA = classA;
    this.a = a;
  }

  public ClassA getClassA() {
    return classA;
  }

  public int getA() {
    return a;
  }
}

동시에 우리가 요구한 ClassA의 첫 번째 변수도 a이다.이 가능하다, ~할 수 있다,...어쨌든 @Provides 방법을 추가해서 ModuleB에 Dagger가 직접 찾도록 하자.
@Module
public class ModuleB {

  private int a;
  private int b;

  public ModuleB(int a, int b) {
    this.a = a;
    this.b = b;
  }

  @Provides
  @Named("a")
  int provideIntA() {
    return a;
  }

  @Provides
  @Named("b")
  int provideIntB() {
    return b;
  }

  @Provides
  ClassA provideClassA(@Named("a") int a, @Named("b") int b) {
    return new ClassA(a, b);
  }

  @Provides
  ClassB provideClassB(ClassA classA, @Named("a") int a) {
    return new ClassB(classA, a);
  }
}

그리고 ClassBComponent 그대로 조롱박을 그린다.
@Component(modules = ModuleB.class)
public interface ClassBComponent {

  void inject(MainActivity mainActivity);
}

그리고 앞의 MainActivity에 대한 ClassA 코드를 삭제합니다.
public class MainActivity extends AppCompatActivity {

  @Inject ClassB classB;
  private static final String TAG = "MainActivity";

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

    DaggerClassBComponent.builder()
        .moduleB(new ModuleB(2, 3))
        .build().inject(this);
    Log.d(TAG, classB.getClassA().getA() + ":" + classB.getClassA().getB() + ":" + classB.getA());
  }
}

실행을 클릭하면 컴파일 오류가 발생할 때까지 모든 것이 완벽해 보입니다.
Error:(10, 8)   : daggerplay.com.daggerplay.classes.ClassB cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.
daggerplay.com.daggerplay.classes.ClassB is injected at
daggerplay.com.daggerplay.MainActivity.classB
daggerplay.com.daggerplay.MainActivity is injected at
daggerplay.com.daggerplay.components.ClassAComponent.inject(mainActivity)

이 잘못된 힌트를 보니 좀 헷갈리는 것 같다. @Provides 이미 표시ClassB했잖아.사실 뒤에 몇 줄이 포인트야.뒤의 몇 줄은 ClassAComponent가 이미 주입MainActivity을 맡았다는 것을 일깨워 준다.ClassAComponentinject 함수를 삭제하였는데, 과연 프로그램이 정상적으로 작동하였다.그럼 도대체 어떻게 된 일입니까?인터넷에 검색해보니 이미 이런 의문이 있었다.https://stackoverflow.com/questions/32341839/multiple-independent-component-injection그리고 깃허브에서도 issue를 제기했다. 한마디로 상관없는 두 사람Component은 같은 대상에 주입하는 것을 책임질 수 없다는 것이다.그럼 질문이 왔습니다. 만약에 우리가 주입ClassA하고 싶고 주입ClassB하고 싶으면 어떻게 합니까?답은 하나Component에 넣는 것이다.문제는 하나Component가 어떻게 여러 대상에 주입됩니까?곰곰이 생각해 보니 사실ClassA의 제공 방법은 이미 ModuleB 안에 있다. Dagger에게 그것을 덧붙여ClassA도 주입하도록 하는 방법이 없을까?네, 사실 간단해요. 예전처럼 MainActivityClassA에 표시하면 돼요.지금은 이전의 ClassAComponentModuleA를 삭제할 수 있다.현재MainActivity는 다음과 같습니다.
public class MainActivity extends AppCompatActivity {
  @Inject ClassA classA;
  @Inject ClassB classB;
  private static final String TAG = "MainActivity";

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

    ClassBComponent daggerClassBComponent = DaggerClassBComponent.builder()
        .moduleB(new ModuleB(2, 3))
        .build();
    daggerClassBComponent.inject(this);
    Log.d(TAG, classA.getClass().getSimpleName());
    Log.d(TAG, classB.getClass().getSimpleName());
  }
}
ModuleB :
@Component(modules = ModuleB.class)
public interface ClassBComponent {

  void inject(MainActivity mainActivity);
}

운행하면 동시에 주입ClassAClassB.만약 하나만 원한다면, 하나의 표시 클래스만 남기면 된다.이것 또한 Dagger를 사용할 때 가장 좋은 습관을 알려준다. 모든 의존을 한 Component 에 한 번에 주입하는 것이다.

코드 생성


아니면 코드 생성을 관례대로 살펴봅시다.rebuild를 기억해라. 그렇지 않으면 원래ClassA의 Dagger 생성 코드가 아직 존재한다.먼저 4개 공장류로 각각 정수 a, 정수 b,ClassAClassB를 제공한다.이전 코드와 같은 방식이어서 붙이지 않겠습니다.그리고 새로운 주입기MainActivity_MembersInjector:
public final class MainActivity_MembersInjector implements MembersInjector {
  private final Provider classAProvider;

  private final Provider classBProvider;

  public MainActivity_MembersInjector(
      Provider classAProvider, Provider classBProvider) {
    assert classAProvider != null;
    this.classAProvider = classAProvider;
    assert classBProvider != null;
    this.classBProvider = classBProvider;
  }

  public static MembersInjector create(
      Provider classAProvider, Provider classBProvider) {
    return new MainActivity_MembersInjector(classAProvider, classBProvider);
  }

  @Override
  public void injectMembers(MainActivity instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.classA = classAProvider.get();
    instance.classB = classBProvider.get();
  }

  public static void injectClassA(MainActivity instance, Provider classAProvider) {
    instance.classA = classAProvider.get();
  }

  public static void injectClassB(MainActivity instance, Provider classBProvider) {
    instance.classB = classBProvider.get();
  }
}

단독 주입ClassAClassB도 가능하다는 것을 알 수 있다.하지만 우리는 보통 이런 종류를 직접 사용하지 않는다.다시 보기DaggerClassBComponent:
public final class DaggerClassBComponent implements ClassBComponent {
  private Provider provideIntAProvider;

  private Provider provideIntBProvider;

  private Provider provideClassAProvider;

  private Provider provideClassBProvider;

  private MembersInjector mainActivityMembersInjector;

  private DaggerClassBComponent(Builder builder) {
    assert builder != null;
    initialize(builder);
  }

  public static Builder builder() {
    return new Builder();
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.provideIntAProvider = ModuleB_ProvideIntAFactory.create(builder.moduleB);

    this.provideIntBProvider = ModuleB_ProvideIntBFactory.create(builder.moduleB);

    this.provideClassAProvider =
        ModuleB_ProvideClassAFactory.create(
            builder.moduleB, provideIntAProvider, provideIntBProvider);

    this.provideClassBProvider =
        ModuleB_ProvideClassBFactory.create(
            builder.moduleB, provideClassAProvider, provideIntAProvider);

    this.mainActivityMembersInjector =
        MainActivity_MembersInjector.create(provideClassAProvider, provideClassBProvider);
  }

  @Override
  public void inject(MainActivity mainActivity) {
    mainActivityMembersInjector.injectMembers(mainActivity);
  }

  public static final class Builder {
    private ModuleB moduleB;

    private Builder() {}

    public ClassBComponent build() {
      if (moduleB == null) {
        throw new IllegalStateException(ModuleB.class.getCanonicalName() + " must be set");
      }
      return new DaggerClassBComponent(this);
    }

    public Builder moduleB(ModuleB moduleB) {
      this.moduleB = Preconditions.checkNotNull(moduleB);
      return this;
    }
  }
}

지금 문제가 하나 있다. 만약 우리가 하나의 단일 대상을 주입하기를 희망한다면 어떻게 실현할 것인가?어떻게 여러 번 초기화되는 것을 방지합니까?Dagger의 @Scope 표시를 언급하지 않을 수 없습니다. 다음에 다시 이 화제를 토론하겠습니다.

좋은 웹페이지 즐겨찾기