Dagger2 사용법

2022. 3. 18. 02:08Android/Library

반응형
  • Android Dagger2 사용방법 3가지
    1. Component Module Scope를 모두 직접 기술해서 사용하는 방법
    2. DispatchingAdnroidInjector를 제공하는 방법
      : AndroidInjectionModule, HasAndroidInjector, DispatchingAndroidInjector
    3. 안드로이드 기반클래스(DaggerApplication, DaggerActivity, DaggerFragment 등)로 제공하는 방법
      : AndroidSupportInjectionModule, DaggerApplication, DaggerActivity, DaggerFragment

 

  • Component Module Scope 직접 기술 
public class MainActivity : AppCompatActivity {

  @Inject
  lateinit var coffee : Coffee

  override fun onCreate(savedInstanceState : Bundle?) {
    super.onCreate(savedInstanceState)

    // 반드시 선행되어야 할 작업, 그렇지 않으면 coffee 멤버변수는 null
    var mainComponent: MainComponent = (application as MyApplication).appComponent
         .mainComponent()
         .create()

    // Member-Injection 유형의 Component함수로 coffee 멤버변수에 의존성 주입
    mainComponent.inject(this@MainActivity)

    // Provision 유형의 Component함수로 Coffee객체 반환(주입)
    var coffee2: Coffee = mainComponent.coffee()
  }
}

 

 

  • DistpatchingAndroidInjector 제공 
@Singleton
@Component(modules = [
    AndroidInjectionModule::class,	// 조건 1번,
    ActivityBindingModule::class, // 조건 2번, AppComponent의 하위 Component 연결위한 Module
    CoffeeModule::class 		// Coffee 의존성 인스턴스 생성 위한 Module
])
interface AppComponent {

    fun inject(myApp : MyApp)	// Application을 구현한 BaseApplication 클래스
}
@Beta
@Module
public abstract class AndroidInjectionModule {
  @Multibinds
  abstract Map<Class<?>, AndroidInjector.Factory<?>> classKeyedInjectorFactories();

  @Multibinds
  abstract Map<String, AndroidInjector.Factory<?>> stringKeyedInjectorFactories();

  private AndroidInjectionModule() {}
}
@Module(subcomponents=[
    MainComponent::class,		// MainActivity Component
    DetailComponent::Class		// DetailActivity Component
])
abstract class ActivityBindingModule {	// @Binds 메서드 사용하기 때문에 abstract(추상) 설정

    @Binds
    @IntoMap				// MultiBinding을 위한 annotation
    @ClassKey(MainActivity::class)	// MultiBinding Map의 Key Type
    abstract fun bindMainInjectorFactory(factory: MainComponent.Factory) : AndroidInjector.Factory<*>

    @Binds
    @IntoMap
    @ClassKey(DetailActivity::class)
    abstract fun bindDetailInjectorFactory(factory: DetailComponent.Factory) : AndroidInjector.Factory<*>
    
    @ActivityScope				// Scope 설정 가능
    @ContributesAndroidInjector(modules=[...]) // modules 설정 가능
    abstract fun subActivity() : SubActivity
}

@Binds @IntoMap annotation으로 생성된 반환 타입은 Map<Class<*>, AndroidInjector.Factory<*>>이다. 

이는 AndroidInjectionModule의 classKeyedInjectorFactories()로 얻을 수 있는 타입과 일치하고, @MultiBinds annotation을 통해 매칭된다.

 

// HasAndroidInjector 인터페이스 상속
class MyApp : Application(), HasAndroidInjector {

	// AppComponent에게 DispatchingAndroidInjector 의존성 요청
    @Inject
    lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>

    override fun onCreate() {
        super.onCreate()
        // AppComponent의 Member-Injection 메서드 inject()
        DaggerAppComponent.create().inject(this@MyApp)
    }

    /** Returns an [AndroidInjector].  */
    // HasAndroidInjector 인터페이스 메서드 구현 (재정의)
    override fun androidInjector(): AndroidInjector<Any>? {
        return dispatchingAndroidInjector
    }
}

DaggerAppComponent는 AppComponent를 상속한 클래스로 Dagger에서 자동으로 생성해준다.

DaggerAppComponent.create() : DaggerAppComponent에 설정한 Module을 모두 설정한 생성자로 생성

.inject(this@MyApp) : 멤버 변수 주입을 위한 AndroidInjector가 제공되어야할 인스턴스 제공

 

class MainActivity : AppCompatActivity() {

    // 의존성 요청할 MultiBinding 타입
    @Inject
    lateinit var coffee: Map<Class<*>, @JvmSuppressWildcards Coffee>

    override fun onCreate(savedInstanceState: Bundle?) {
        // AndroidInjection.inject() 메서드 한번으로 Dagger 의존성 주입 (super.onCreate() 전에 사용 권장)
        AndroidInjection.inject(this@MainActivity)

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        println(coffee[Espresso::class.java]?.name())
    }
}

MainActivity 또한 주입을 받아야 할 멤버 변수가 있기에 AndroidInjection.inject(this@MainActivity)로 인스턴스를 제공한다.

 

참고>

@DaggerGenerated
@SuppressWarnings({
    "unchecked",
    "rawtypes"
})
public final class DaggerAppComponent implements AppComponent {
  private final DaggerAppComponent appComponent = this;

  private DaggerAppComponent() {


  }

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

  public static AppComponent create() {
    return new Builder().build();
  }

  public static final class Builder {
    private Builder() {
    }

    public AppComponent build() {
      return new DaggerAppComponent();
    }
  }
}

 

 

  • 안드로이드 기반클래스(DaggerApplication, DaggerActivity, DaggerFragment 등)로 제공 
@Component(modules = [
        AndroidSupportInjectionModule::class,	// 1️⃣ 
        ActivityBindingModule::class,
        CoffeeModule::class
])
interface AppComponent : AndroidInjector<MyApp> {	// 2️⃣
	// 3️⃣ 
}

AndroidInjector를 상속함으로 Member-Injection인 inject()함수를 없애고 위임한다.

 

class MyApp : DaggerApplication() {

    override fun onCreate() {
        super.onCreate()
    }

    // DaggerApplication의 메서드 구현(재정의) - AppComponent는 AndroidInjector를 상속했기에 반환Type에 캐스팅
    override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
        return DaggerAppComponent.create()	// AppComponent 인스턴스 반환
    }

}

MyApp이 DaggerApplication을 상속했기 때문에 AndroidInjector<MyApp>의 상속을 받는 AppComponent의 인스턴스를 반환하더라도 AndroidInjector<DaggerApplication> 으로 추상화가 성립한다.

 

class MainActivity : DaggerActivity() {	// DaggerActivity() 상속

    @Inject
    lateinit var coffee: Map<Class<*>, @JvmSuppressWildcards Coffee>

    override fun onCreate(savedInstanceState: Bundle?) {

        // AndroidInjection.inject(this@MainActivity)

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

 

참고>

@Beta
public abstract class DaggerApplication extends Application implements HasAndroidInjector {
  @Inject volatile DispatchingAndroidInjector<Object> androidInjector;

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

  /**
   * Implementations should return an {@link AndroidInjector} for the concrete {@link
   * DaggerApplication}. Typically, that injector is a {@link dagger.Component}.
   */
  @ForOverride
  protected abstract AndroidInjector<? extends DaggerApplication> applicationInjector();

  /**
   * Lazily injects the {@link DaggerApplication}'s members. Injection cannot be performed in {@link
   * Application#onCreate()} since {@link android.content.ContentProvider}s' {@link
   * android.content.ContentProvider#onCreate() onCreate()} method will be called first and might
   * need injected members on the application. Injection is not performed in the constructor, as
   * that may result in members-injection methods being called before the constructor has completed,
   * allowing for a partially-constructed instance to escape.
   */
  private void injectIfNecessary() {
    if (androidInjector == null) {
      synchronized (this) {
        if (androidInjector == null) {
          @SuppressWarnings("unchecked")
          AndroidInjector<DaggerApplication> applicationInjector =
              (AndroidInjector<DaggerApplication>) applicationInjector();
          applicationInjector.inject(this);
          if (androidInjector == null) {
            throw new IllegalStateException(
                "The AndroidInjector returned from applicationInjector() did not inject the "
                    + "DaggerApplication");
          }
        }
      }
    }
  }

  @Override
  public AndroidInjector<Object> androidInjector() {
    // injectIfNecessary should already be called unless we are about to inject a ContentProvider,
    // which can happen before Application.onCreate()
    injectIfNecessary();

    return androidInjector;
  }
}

App은 Application을 직접 상속받지 않고, Application을 상속받은 DaggerApplication을 상속받는다.

DaggerApplication은 androidInjector.inject(this)가 구현되어 있는 클래스이며, 해당 androidInjector는 AndroidInjector<DaggerApplication> 타입이며, androidInjector() 호출을 통해 할당 받는다.

androidInjector()는 App에서 오버라이드 되어 return DaggerAppComponent.create()로 DaggerAppComponent가 생성되고, 이는 AppComponent로 추상화 되어 반환된다.

AppComponent는 AndroidInjector<App>을 상속한 클래스이므로, AndroidInjector<DaggerApplication>으로 추상화되어 할당가능하다.

 

@DaggerGenerated
@SuppressWarnings({
    "unchecked",
    "rawtypes"
})
public final class DaggerAppComponent implements AppComponent {
  private final UseCaseModule useCaseModule;

  private final RepositoryModule repositoryModule;

  private final DaggerAppComponent appComponent = this;

  private Provider<ActivityBindingModule_MainActivity.MainActivitySubcomponent.Factory> mainActivitySubcomponentFactoryProvider;

  private Provider<Application> applicationProvider;

  private Provider<Context> bindContextProvider;

  private Provider<LocalMovieDatabase> provideLocalMovieDatabaseProvider;

  private Provider<OkHttpClient> provideOkHttpClientProvider;

  private Provider<GsonConverterFactory> provideGsonConvertFactoryProvider;

  private Provider<Retrofit> provideRetrofitProvider;

  private Provider<TMDBService> provideTMDBServiceProvider;

  private Provider<PrefHelper> providePrefHelperProvider;

  private Provider<PopularMovieDao> providePopularMovieDaoProvider;

  private DaggerAppComponent(RetrofitModule retrofitModuleParam, RoomModule roomModuleParam,
      UseCaseModule useCaseModuleParam, RepositoryModule repositoryModuleParam,
      Application applicationParam) {
    this.useCaseModule = useCaseModuleParam;
    this.repositoryModule = repositoryModuleParam;
    initialize(retrofitModuleParam, roomModuleParam, useCaseModuleParam, repositoryModuleParam, applicationParam);

  }

  public static AppComponent.Factory factory() {
    return new Factory();
  }

  private Map<Class<?>, Provider<dagger.android.AndroidInjector.Factory<?>>> mapOfClassOfAndProviderOfAndroidInjectorFactoryOf(
      ) {
    return Collections.<Class<?>, Provider<dagger.android.AndroidInjector.Factory<?>>>singletonMap(MainActivity.class, ((Provider) mainActivitySubcomponentFactoryProvider));
  }

  private DispatchingAndroidInjector<Object> dispatchingAndroidInjectorOfObject() {
    return DispatchingAndroidInjector_Factory.newInstance(mapOfClassOfAndProviderOfAndroidInjectorFactoryOf(), Collections.<String, Provider<dagger.android.AndroidInjector.Factory<?>>>emptyMap());
  }

  private RefreshControl refreshControl() {
    return RepositoryModule_ProvideRefreshControlFactory.provideRefreshControl(repositoryModule, providePrefHelperProvider.get());
  }

  private Pager<Integer, PopularMovieLocal> pagerOfIntegerAndPopularMovieLocal() {
    return RepositoryModule_ProvidePopularMoviesPagerFactory.providePopularMoviesPager(repositoryModule, RepositoryModule_ProvidePagingConfigFactory.providePagingConfig(repositoryModule), provideLocalMovieDatabaseProvider.get(), provideTMDBServiceProvider.get(), refreshControl(), providePopularMovieDaoProvider.get());
  }

  private PopularMovieRepository popularMovieRepository() {
    return RepositoryModule_ProvidePopularMovieRepositoryFactory.providePopularMovieRepository(repositoryModule, provideLocalMovieDatabaseProvider.get(), provideTMDBServiceProvider.get(), pagerOfIntegerAndPopularMovieLocal());
  }

  private GetPopularMovies getPopularMovies() {
    return UseCaseModule_ProvideGetPopularMoviesFactory.provideGetPopularMovies(useCaseModule, popularMovieRepository());
  }

  @SuppressWarnings("unchecked")
  private void initialize(final RetrofitModule retrofitModuleParam,
      final RoomModule roomModuleParam, final UseCaseModule useCaseModuleParam,
      final RepositoryModule repositoryModuleParam, final Application applicationParam) {
    this.mainActivitySubcomponentFactoryProvider = new Provider<ActivityBindingModule_MainActivity.MainActivitySubcomponent.Factory>() {
      @Override
      public ActivityBindingModule_MainActivity.MainActivitySubcomponent.Factory get() {
        return new MainActivitySubcomponentFactory(appComponent);
      }
    };
    this.applicationProvider = InstanceFactory.create(applicationParam);
    this.bindContextProvider = DoubleCheck.provider((Provider) applicationProvider);
    this.provideLocalMovieDatabaseProvider = DoubleCheck.provider(RoomModule_ProvideLocalMovieDatabaseFactory.create(roomModuleParam, bindContextProvider));
    this.provideOkHttpClientProvider = DoubleCheck.provider(RetrofitModule_ProvideOkHttpClientFactory.create(retrofitModuleParam));
    this.provideGsonConvertFactoryProvider = DoubleCheck.provider(RetrofitModule_ProvideGsonConvertFactoryFactory.create(retrofitModuleParam));
    this.provideRetrofitProvider = DoubleCheck.provider(RetrofitModule_ProvideRetrofitFactory.create(retrofitModuleParam, bindContextProvider, provideOkHttpClientProvider, provideGsonConvertFactoryProvider));
    this.provideTMDBServiceProvider = DoubleCheck.provider(RetrofitModule_ProvideTMDBServiceFactory.create(retrofitModuleParam, provideRetrofitProvider));
    this.providePrefHelperProvider = DoubleCheck.provider(AppModule_Companion_ProvidePrefHelperFactory.create(bindContextProvider));
    this.providePopularMovieDaoProvider = DoubleCheck.provider(RoomModule_ProvidePopularMovieDaoFactory.create(roomModuleParam, provideLocalMovieDatabaseProvider));
  }

  @Override
  public void inject(App arg0) {
    injectApp(arg0);
  }

  private App injectApp(App instance) {
    DaggerApplication_MembersInjector.injectAndroidInjector(instance, dispatchingAndroidInjectorOfObject());
    return instance;
  }

  private static final class Factory implements AppComponent.Factory {
    @Override
    public AppComponent create(Application application) {
      Preconditions.checkNotNull(application);
      return new DaggerAppComponent(new RetrofitModule(), new RoomModule(), new UseCaseModule(), new RepositoryModule(), application);
    }
  }

  private static final class MainActivitySubcomponentFactory implements ActivityBindingModule_MainActivity.MainActivitySubcomponent.Factory {
    private final DaggerAppComponent appComponent;

    private MainActivitySubcomponentFactory(DaggerAppComponent appComponent) {
      this.appComponent = appComponent;
    }

    @Override
    public ActivityBindingModule_MainActivity.MainActivitySubcomponent create(MainActivity arg0) {
      Preconditions.checkNotNull(arg0);
      return new MainActivitySubcomponentImpl(appComponent, new MainActivityModule(), arg0);
    }
  }

  private static final class MainActivitySubcomponentImpl implements ActivityBindingModule_MainActivity.MainActivitySubcomponent {
    private final MainActivityModule mainActivityModule;

    private final MainActivity arg0;

    private final DaggerAppComponent appComponent;

    private final MainActivitySubcomponentImpl mainActivitySubcomponentImpl = this;

    private MainActivitySubcomponentImpl(DaggerAppComponent appComponent,
        MainActivityModule mainActivityModuleParam, MainActivity arg0Param) {
      this.appComponent = appComponent;
      this.mainActivityModule = mainActivityModuleParam;
      this.arg0 = arg0Param;

    }

    private MainViewModel mainViewModel() {
      return new MainViewModel(appComponent.getPopularMovies());
    }

    private PagingAdapter pagingAdapter() {
      return MainActivityModule_ProvidePagingAdapterFactory.providePagingAdapter(mainActivityModule, arg0);
    }

    @Override
    public void inject(MainActivity arg0) {
      injectMainActivity(arg0);
    }

    private MainActivity injectMainActivity(MainActivity instance) {
      DaggerAppCompatActivity_MembersInjector.injectAndroidInjector(instance, appComponent.dispatchingAndroidInjectorOfObject());
      MainActivity_MembersInjector.injectViewModel(instance, mainViewModel());
      MainActivity_MembersInjector.injectAdapter(instance, pagingAdapter());
      return instance;
    }
  }
}

androidInjector() 호출로 선언된 Module을 인자로 넘겨받은 DaggerAppComponent의 생성자를 통해 initialize()가 호출되어 주입될 멤버 변수들의 Provider 생성 초기화가 진행된다.

 

DaggerAppCompatActivity_MembersInjector
	.injectAndroidInjector(instance, appComponent.dispatchingAndroidInjectorOfObject())

inject(app)를 호출하게 되면 instance로 넘겨진 App 인스턴스의 androidInjector 멤버 변수에 dispatchingAndroidInjectorOfObject()를 할당한다.

 

DispatchingAndroidInjector_Factory
	.newInstance(mapOfClassOfAndProviderOfAndroidInjectorFactoryOf(), 
    	Collections.<String, Provider<dagger.android.AndroidInjector.Factory<?>>>emptyMap())

dispatchingAndroidInjectorOfObject()는 두 인자를 merge한 Map 형태의 DispatchingAndroidInjector를 새로 생성한다.

 

Collections.<Class<?>, Provider<dagger.android.AndroidInjector.Factory<?>>>
	singletonMap(MainActivity.class, ((Provider) mainActivitySubcomponentFactoryProvider));

mapOfClassOfAndProviderOfAndroidInjectorFactoryOf()는 initialize() 호출 시 초기화 되었던 멤버 변수 중 Provider<AndroidInjector.Factory> 형태를 해당 Class와 묶어서 Map으로 자동생성해 제공해준다.(Bind한 Activity, Fragment 등 개수에 따라 Map을 자동 생성해줌)

 

@Beta
public abstract class DaggerAppCompatActivity extends AppCompatActivity
    implements HasAndroidInjector {

  @Inject DispatchingAndroidInjector<Object> androidInjector;

  public DaggerAppCompatActivity() {
    super();
  }

  @ContentView
  public DaggerAppCompatActivity(@LayoutRes int contentLayoutId) {
    super(contentLayoutId);
  }

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

  @Override
  public AndroidInjector<Object> androidInjector() {
    return androidInjector;
  }
}

 

마찬가지로, AppCompatActivity를 상속받는 DaggerAppCompatActivity를 상속받아 AndroidInjection.inject(this)를 별도 구현할 필요 없이 사용하면 된다.
(DaggerActivity를 상속받으면 코루틴에서 필요한 lifecyclescope을 사용할 수 없음)

반응형

'Android > Library' 카테고리의 다른 글

Lint - Android Studio 내장 Lint vs. Ktlint  (0) 2022.04.23
Dagger2  (1) 2022.03.17