Dagger2를 해석해 드릴게요.

22673 단어 의존 주입Dagger2
1. Dagger2 소개:
Dagger2는 Dagger1의 지점으로 구글이 인수하여 개발한 것으로 현재 버전은 2.0이다.Dagger2는 AutoValue 프로젝트에 영감을 받았습니다.처음에 Dagger2가 문제를 해결하는 기본 사상은 생성과 쓴 코드를 혼합하여 모든 생성과 의존을 제공하는 것처럼 보이는 코드가 손으로 쓴 것처럼 보이도록 하는 것이다.
Dagger2는 다음과 같은 장점을 가진다. 1) 의존적인 주입과 설정은 구성 요소 외에 독립되고 주입된 대상은 독립적이고 결합되지 않은 곳에서 초기화된다. 이렇게 하면 주입된 대상을 바꿀 때 우리는 대상의 실현 방법을 수정하고 코드 라이브러리를 크게 바꾸지 않아도 된다.2) 의존은 하나의 구성 요소에 주입할 수 있다. 우리는 이러한 의존의 시뮬레이션을 주입하여 테스트를 더욱 간단하게 할 수 있다.3) 앱의 구성 요소는 실례적인 창설과 생명주기에 관한 어떤 것도 알 필요가 없다. 이것은 우리의 의존이 프레임워크에 주입되어 관리된다.Gger2와 같은 의존 주입 프레임워크는 MVP 구조에 있어 가장 좋은 결합 해체 도구로modle-view-presenter 간의 결합도를 더욱 낮출 수 있다.
2.의존 주입이란 무엇인가
만약에 Class A에 Class B의 실례가 있다면 Class A는 Class B에 대한 의존도가 있다고 한다.예를 들어 다음 클래스Human에서Father 대상을 사용하면 클래스Human은 클래스Father에 의존한다고 할 수 있다.
public class Human {
    ...
    Father father;
    ...
    public Human() {
        father = new Father();
    }
}

의존 주입은 무엇입니까? 의존 주입은 스스로 의존을 초기화하지 않고 외부를 통해 의존을 전달하는 방식입니다. 쉽게 말하면 new를 사용하지 않고 의존 대상을 만드는 것입니다.Dagger2를 사용하여 종속 객체를 만들면 수동으로 초기화할 필요가 없습니다.개인적으로 Dagger2와 MVP 구조가 비교적 좋은 조합이라고 생각합니다.Activity가 의존하는 Presenter는 이DI 프레임워크를 직접 생성하여 결합을 실현할 수 있습니다. 간단한 사용 방식은 다음과 같습니다.
public class MainActivity extends BaseActivity {
      @Inject
       MainActivityPresenter presenter;

     ...  

}

위와 같은 것들은 주로DI틀에 대한 초보적인 전체적인 이해를 가지고 다음은Dagger2의 기본적인 내용을 살펴본다.Dagger2는 주석을 통해 코드를 생성하고 서로 다른 역할을 정의한다. 주요한 주석은 @Inject, @Module, @Component, @Provides, @Scope, @SubComponent 등이 있다.
3. Dagger2 참고 사항: Dagger2의 각 개념에 대해 살펴보겠습니다.
@Inject: 일반적으로 이 주석을 의존해야 하는 곳에 사용합니다.다시 말하면, Dagger라는 클래스나 필드는 주입에 의존해야 한다고 알려준다.이렇게 하면 Dagger는 이러한 종류의 실례를 만들어서 그들의 의존을 만족시킬 것입니다. @Module: Modules 클래스의 방법은 의존을 제공하기 때문에 우리는 하나의 클래스를 정의하고 @Module 주석을 사용하면 Dagger가 클래스의 실례를 구성할 때 필요한 의존을 어디서 찾을 수 있는지 알 수 있습니다.modules의 중요한 특징은 구역을 나누어 조합하는 것이다. (예를 들어 우리 앱에서 여러 개의 모듈을 구성할 수 있다.)
@Provide:modules에서 우리가 정의한 방법은 이 주석을 사용해서 Dagger에게 우리가 대상을 구성하고 의존을 제공하고자 한다는 것을 알려 주는 것입니다.
@Component: Components는 근본적으로 주입기일 수도 있고 @Inject와 @Module의 다리라고 할 수도 있는데 그 주요 역할은 이 두 부분을 연결하는 것이다.Components는 모든 정의된 유형의 실례를 제공할 수 있습니다. 예를 들어, 우리는 @Component로 인터페이스를 설명한 다음에 모든 @Modules를 열거해서 이 구성 요소를 구성해야 합니다. 이 구성 요소가 없으면 컴파일할 때 오류가 발생합니다.모든 구성 요소는 모듈을 통해 의존의 범위를 알 수 있습니다.
@Scope: Scopes는 매우 유용합니다. Dagger2는 주석의 역할 영역을 사용자 정의로 제한할 수 있습니다.뒤에 하나의 예를 보여 줄 것이다. 이것은 매우 강한 특징이다. 앞에서 말한 바와 같이 모든 대상이 그들의 실례를 어떻게 관리하는지 이해할 필요가 없기 때문이다.scope의 예에서, 우리는 사용자 정의 @PerActivity로 클래스를 주석하기 때문에, 이 대상의 생존 시간은 activity와 같다.쉽게 말하면 우리는 모든 범위의 입도를 정의할 수 있다. (@PerFragment, @PerUser 등)
Qualifier: 의존을 감별하기 위해 클래스의 종류가 부족할 때 이 주석 표시를 사용할 수 있습니다.예를 들어 안드로이드에서 우리는 서로 다른 종류의 context가 필요하기 때문에qualifier 주석 '@Forapplication' 과 '@ForActivity' 를 정의할 수 있습니다. 이렇게 하면 context를 주입할 때 Dagger에게 우리가 어떤 종류의 context를 원하는지 알려줄 수 있습니다.
4. Dagger2 사용 방법
우선 우리build에서gradle 파일에는 다음과 같은 구성이 있습니다.
apply plugin: 'com.neenbedankt.android-apt'

buildscript {
  repositories {
    jcenter()
  }
  dependencies {
classpath 'com.android.tools.build:gradle:1.5.0'
    classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
  }
}

android {
  ...
}

dependencies {
  apt 'com.google.dagger:dagger-compiler:2.0'
  compile 'com.google.dagger:dagger:2.0'

  ...
}

컴파일링과 실행 라이브러리를 추가했습니다. 이 플러그인이 없으면, Dagger가 정상적으로 작동하지 않을 수도 있습니다. 특히 안드로이드 스튜디오에서.
Application Component: 라이프 사이클이 Application과 동일한 컴포넌트입니다.AndroidApplication 및 BaseActivity 클래스에 주입할 수 있습니다.
@Singleton // Constraints this component to one-per-application or unscoped bindings.
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
  void inject(BaseActivity baseActivity);

  //Exposed to sub-graphs.
  Context context();
  ThreadExecutor threadExecutor();
  PostExecutionThread postExecutionThread();
  UserRepository userRepository();
}

@Singleton 주석을 사용하여 유일성을 보장합니다.내가 왜 context와 다른 멤버들을 폭로해야 하는지 물어볼지도 몰라.이것이 바로 Dagger에서components 작업의 중요한 성질이다. 모듈의 유형을 노출시키고 싶지 않으면, 모듈을 표시해서만 사용할 수 있다.이 예에서, 나는 이 요소들을 하위 그림에 폭로했다. 만약 네가 그들을 삭제한다면, 번역할 때 잘못 보고할 것이다.
Application Module: Application Component에 주입할 대상을 제공합니다.이것도 바로 @Provide 주석의 방법을 @Singleton으로 한정하는 이유입니다.
@Module
public class ApplicationModule {
  private final AndroidApplication application;

  public ApplicationModule(AndroidApplication application) {
    this.application = application;
  }

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

  @Provides @Singleton Navigator provideNavigator() {
    return new Navigator();
  }

  @Provides @Singleton ThreadExecutor provideThreadExecutor(JobExecutor jobExecutor) {
    return jobExecutor;
  }

  @Provides @Singleton PostExecutionThread providePostExecutionThread(UIThread uiThread) {
    return uiThread;
  }

  @Provides @Singleton UserCache provideUserCache(UserCacheImpl userCache) {
    return userCache;
  }

  @Provides @Singleton UserRepository provideUserRepository(UserDataRepository userDataRepository) {
    return userDataRepository;
  }
}

Activity Component: 라이프 사이클이 Activity와 같은 구성 요소입니다.
@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
  //Exposed to sub-graphs. Activity activity();
}


@PerActivity는 사용자 정의 범위 주석으로 대상이 정확한 구성 요소에 기록될 수 있도록 하는 역할을 한다. 물론 이 대상들의 생명주기는activity의 생명주기에 따라야 한다.
Activity Module: 대상 그림에서 이 모듈은 관련 클래스에 activity를 노출합니다.예를 들어fragment에서activity의context를 사용합니다.
@Module
public class ActivityModule {
  private final Activity activity;

  public ActivityModule(Activity activity) {
    this.activity = activity;
  }

  @Provides @PerActivity Activity activity() {
    return this.activity;
  }
}

User Component:Activity Component의 구성 요소를 계승하고 @PerActivity 주석을 사용합니다.나는 보통 사용자와 관련된fragment에 주입해서 사용한다.Activity Module가 그림에 activity를 노출시켰기 때문에, 어떤 activity의context가 필요할 때든 Dagger는 주입을 제공할 수 있으며, 하위 모듈에 정의할 필요가 없습니다.
@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = {ActivityModule.class, UserModule.class})
public interface UserComponent extends ActivityComponent {
  void inject(UserListFragment userListFragment);
  void inject(UserDetailsFragment userDetailsFragment);
}


User Module: 사용자와 관련된 인스턴스를 제공합니다.우리의 예를 바탕으로 사용자 용례를 제공할 수 있습니다.
@Module
public class UserModule {
  @Provides @PerActivity GetUserListUseCase provideGetUserListUseCase(GetUserListUseCaseImpl getUserListUseCase) {
    return getUserListUseCase;
  }

  @Provides @PerActivity GetUserDetailsUseCase provideGetUserDetailsUseCase(GetUserDetailsUseCaseImpl getUserDetailsUseCase) {
    return getUserDetailsUseCase;
  }
}

통합: Dagger는 다음과 같은 선택의 폭을 제공합니다.
구조 방법 주입: 클래스의 구조 방법 앞에서 @Inject 구성원 변수 주입: 클래스의 구성원 변수 (비공유) 앞에서 @Inject 함수 방법 주입: 함수 앞에서 @Inject
이 순서는 Dagger가 권장하는 순서입니다. 실행하는 과정에서 이상한 문제나 빈 바늘이 있을 수 있기 때문입니다. 이것은 대상을 만들 때 의존도가 초기화되지 않았을 수도 있다는 것을 의미합니다.이것은 안드로이드의activity나fragment에서 구성원 변수를 사용하여 주입하는 것을 자주 볼 수 있습니다. 왜냐하면 우리는 그들의 구조 방법에서 사용하지 않기 때문입니다.
BaseActivity에 구성원 변수를 어떻게 주입하는지 보십시오.이 예에서 우리는 Navigator라는 클래스를 주입했는데 이것은 우리 응용 프로그램에서 내비게이션을 관리하는 클래스이다.
public abstract class BaseActivity extends Activity {

  @Inject Navigator navigator;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    this.getApplicationComponent().inject(this);
  }

  protected ApplicationComponent getApplicationComponent() {
    return ((AndroidApplication)getApplication()).getApplicationComponent();
  }

  protected ActivityModule getActivityModule() {
    return new ActivityModule(this);
  }
}

Navigator 클래스는 Application Module의 @Provide 주석에 의해 제공되는 구성원 변수로 주입됩니다.최종적으로 우리는component를 초기화하고 inject () 방법을 사용하여 구성원 변수를 주입합니다.Activity의 onCreate () 방법에서 getapplicationComponent () 를 호출해서 이 작업을 완성합니다.getapplicationComponent () 방법은 복용성을 위한 것이고, 그 주요 역할은 실례화된 ApplicationComponent 대상을 얻기 위한 것이다.
Fragment의presenter에서 우리도 같은 일을 했습니다. 여기서 얻는 방법은 조금 다릅니다. 왜냐하면per-activity 범위가 한정된component를 사용하느냐고 물었기 때문입니다.그래서 저희가 User Details Fragment에 주입된 User Component는 사실 User Details Activity에 주재하고 있습니다.
private UserComponent userComponent;

activity의 onCreate () 방법에서 아래의 방식으로 초기화해야 합니다.
private void initializeInjector() {
  this.userComponent = DaggerUserComponent.builder()
      .applicationComponent(getApplicationComponent())
      .activityModule(getActivityModule())
      .build();
}

Dagger는 components를 생성하고 이름을 바꾸는 데 'Dagger' 접두사를 붙이는 주석을 처리합니다.이것은 하나의 조합의component이기 때문에 구축할 때 우리는 모든 의존을 전송해야 한다. (components와modules)현재 우리의component는 이미 준비가 되어 있습니다. 이어서fragment의 의존 수요를 충족시키기 위해 우리는 얻는 방법을 씁니다.
@Override public UserComponent getComponent() {
  return userComponent;
}

get 방법으로 만든component를 가져오고 inject () 방법으로 Fragment을 매개 변수로 전송하면 귀속UserDetailsFragment 의존이 완료됩니다.
@Override public void onActivityCreated(Bundle savedInstanceState) {
  super.onActivityCreated(savedInstanceState);
  this.getComponent.inject(this);
}


public interface HasComponent<C> {
  C getComponent();
}

따라서 클라이언트(예를 들어fragment)는component(activity에서)를 가져오고 사용할 수 있습니다.
@SuppressWarnings("unchecked")
protected <C> C getComponent(Class<C> componentType) {
  return componentType.cast(((HasComponent<C>)getActivity()).getComponent());
}

이 클라이언트가 사용할 수 있는component를 얻을 수 없든지 간에 강제 변환을 사용했습니다. 적어도 곧 실패할 것입니다.

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerApplicationComponent implements ApplicationComponent {
  private Provider<Navigator> provideNavigatorProvider;
  private MembersInjector<BaseActivity> baseActivityMembersInjector;

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

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

  private void initialize(final Builder builder) {  
    this.provideNavigatorProvider = ScopedProvider.create(ApplicationModule_ProvideNavigatorFactory.create(builder.applicationModule));
    this.baseActivityMembersInjector = BaseActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), provideNavigatorProvider);
  }

  @Override
  public void inject(BaseActivity baseActivity) {  
    baseActivityMembersInjector.injectMembers(baseActivity);
  }

  public static final class Builder {
    private ApplicationModule applicationModule;

    private Builder() {  
    }

    public ApplicationComponent build() {  
      if (applicationModule == null) {
        throw new IllegalStateException("applicationModule must be set");
      }
      return new DaggerApplicationComponent(this);
    }

    public Builder applicationModule(ApplicationModule applicationModule) {  
      if (applicationModule == null) {
        throw new NullPointerException("applicationModule");
      }
      this.applicationModule = applicationModule;
      return this;
    }
  }
}

주의해야 할 두 가지 중점이 있다.첫 번째: activity에 의존을 주입해야 하기 때문에 이 구성원에 대한 주입기를 주입합니다. (Dagger에서 생성된 BaseActivity MembersInjector)
@Generated("dagger.internal.codegen.ComponentProcessor")
public final class BaseActivity_MembersInjector implements MembersInjector<BaseActivity> {
  private final MembersInjector<Activity> supertypeInjector;
  private final Provider<Navigator> navigatorProvider;

  public BaseActivity_MembersInjector(MembersInjector<Activity> supertypeInjector, Provider<Navigator> navigatorProvider) {  
    assert supertypeInjector != null;
    this.supertypeInjector = supertypeInjector;
    assert navigatorProvider != null;
    this.navigatorProvider = navigatorProvider;
  }

  @Override
  public void injectMembers(BaseActivity instance) {  
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    supertypeInjector.injectMembers(instance);
    instance.navigator = navigatorProvider.get();
  }

  public static MembersInjector<BaseActivity> create(MembersInjector<Activity> supertypeInjector, Provider<Navigator> navigatorProvider) {  
      return new BaseActivity_MembersInjector(supertypeInjector, navigatorProvider);
  }
}

이 주입기는 일반적으로 모든activity의 주입 구성원에게 의존을 제공합니다. inject () 방법을 호출하기만 하면 필요한 필드와 의존을 얻을 수 있습니다.두 번째 중점: 우리의Dagger Application Component 클래스에 대해서는Provider가 있습니다. 이것은 실례를 제공하는 인터페이스가 아니라 Scoped Provider에 의해 만들어진 것으로 실례를 만드는 범위를 기록할 수 있습니다.Dagger는 Navigator 클래스에 Application Module 라는 이름도 생성합니다.Provide Navigator Factory의 공장입니다. 이 공장은 위에서 언급한 범위 파라미터를 전달하고 이 범위 내의 클래스의 실례를 얻을 수 있습니다.
@Generated("dagger.internal.codegen.ComponentProcessor")
public final class ApplicationModule_ProvideNavigatorFactory implements Factory<Navigator> {
  private final ApplicationModule module;

  public ApplicationModule_ProvideNavigatorFactory(ApplicationModule module) {  
    assert module != null;
    this.module = module;
  }

  @Override
  public Navigator get() {  
    Navigator provided = module.provideNavigator();
    if (provided == null) {
      throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");
    }
    return provided;
  }

  public static Factory<Navigator> create(ApplicationModule module) {  
    return new ApplicationModule_ProvideNavigatorFactory(module);
  }
}

이 클래스는 매우 간단합니다. 이것은 Application Module (@Provide 방법 포함) 에서Navigator 클래스를 만들었음을 의미합니다.한 마디로 하면 위의 코드는 마치 손으로 두드린 것처럼 보일 뿐만 아니라 매우 이해하기 쉬워 디버깅하기 편리하다.나머지는 탐색할 수 있는 것이 많습니다. 디버깅을 통해 Dagger가 의존 귀속을 어떻게 완성하는지 보십시오.

좋은 웹페이지 즐겨찾기