2022. 3. 18. 02:08ㆍAndroid/Library
- Android Dagger2 사용방법 3가지
- Component Module Scope를 모두 직접 기술해서 사용하는 방법
- DispatchingAdnroidInjector를 제공하는 방법
: AndroidInjectionModule, HasAndroidInjector, DispatchingAndroidInjector - 안드로이드 기반클래스(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 |