-
Dagger 는 static, compile-time dependency injection framework 이다.
기존 버전(1.x)은 Square 에 의해 만들어졌고, 새 버전은 (2.x) Google 에 의해 유지보수되고 있다.
-
Hard dependency 는..
reusability 를 감소시킨다.
testing 을 어렵게 한다.
코드의 scale up 이나 유지보수를 어렵게 한다.
-
Dependency 에는 다음의 type 이 있다.
class, interface ,method/field, direct/indirect
-
java 에서 new operator 로 instance 를 생성하면, 독립적으로 test 되기가 어렵다. 이를 dependency 라 불린다.
-
Dependency Injection 이라 함은.. 하나의 object 가 다른 object 에 dependency 형태로 제공되는 기술을 말한다.
Dependency 는 사용되는 object 를 말하고, injection 은 이 dependency 를 다른 곳으로 전달하는 것을 말한다.
Inversion of control 컨셉을 이용한다.
-
dependency injection 에 의존하는 앱에서 objects 들은 주입이 되고, 사용자가 스스로 생성하지 않는다.
-
Dagger 는 reflection 을 쓰는 것이 아니라 compile time 에 annotation processor 를 통해 코드생성을 한다.
Dagger2 annotation 을 이해하기.
-
@Inject 는 JSR-330 spec 을 따르며 injection 을 받는 곳을 가르킨다.
Constructor, Field, Method injection 이 있다.
public class Starts{
@Inject
Allies allies;
@Inject // constructor injection 은 해당 class 가 @Inject 로 마킹된 곳에 자동 inject 된다. (module 에 정의하지 않아도)
public Starts(){
}
@Inject
private void prepareForWar(){
}
}
-
@Componnet annotation 은 모든 것을 연결해주는 interface 이다.
@Module 과 @Inject 의 다리 역할을 한다.
@Module 에 정의된 것을 @Inject 가 정의된 곳에 inject 해주는 역할을 한다.
-
@Component 로 마킹된 녀석은
Dagger<OriginalClassName> 으로 코드가 생성되며, create() method 를 갖는다.
( 예를 들어 ABC 라는 class(혹은 interface) 가 있고, @Component 로 마킹되면 DaggerABC class 가 생성된다. )
-
public class Starks{
@Inject
public Starks(){ }
}
public class Boltons{
@Inject
public Boltons(){ }
}
public class War{
@Inject
public War(Starks starks, Boltons bolton){
..
}
}
@Component
interface BattleComponent{
War getWar();
}
// 사용
BattleComponent component = DaggerBattleComponent.create();
// injecting 이 자동으로 된 녀석이 나온다.
// War 의 constructor 에 @Inject 가 마킹되었기 때문이며,
// War 의 Starks 와 Boltons 의 constructor 에 @Inject 가 마킹되어 자동으로 inject, inject 되어 나온다.War war = component.getWar();
-
DaggerBattleComponent 안으로 jump 해서 들어가면 생성된 코드를 볼 수 있다.
-
@Module 은 inject 될 것을 provide 해주는 역할을 한다.
Android 를 예로 들면 ContextModule 이라는 클래스를 정의하면서 @Module 을 사용하면 ApplicationContext 나 Context 등을 제공해줄 수 있다.
-
@Provides 는 module 안쪽 method 에 marking 을 하는데 사용하며, dependency 를 실제로 제공해주는 역할을 한다.
-
@Module
public class BraavosModule {
Cash cash;
Soldiers soldiers;
public BraavosModule(Cash cash, Soldiers soldiers){
this.cash=cash;
this.soldiers=soldiers;
}
@Provides //Provides cash dependency
Cash provideCash(){
return cash;
}
@Provides //provides soldiers dependency
Soldiers provideSoldiers(){
return soldiers;
}
}
@Component 정의할 때 module 을 지정할 수 있다.
@Component(modules = BraavosModule.class)
interface BattleComponent{
War getWar();
Cash getCash();
Soldiers getSoldiers();
}
사용할 때는..
DaggerBattleComponent.builder().braavosModule(new BraavosModule(cash, soldiers)).build();
// BraavosModule 을 제공해주어야 한다.
Advanced Dagger2
https://medium.com/@harivigneshjayapalan/dagger-2-for-android-beginners-advanced-part-i-1e14fccf2cc8
위 링크를 통해 제대로 설명, 코드, 그래프를 보는 것이 추천된다.
-
public class MainActivity extends AppCompatActivity {
Retrofit retrofit;
RecyclerView recyclerView;
RandomUserAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initViews();
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
Timber.plant(new Timber.DebugTree());
HttpLoggingInterceptor httpLoggingInterceptor = new
HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(@NonNull String message) {
Timber.i(message);
}
});
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient okHttpClient = new OkHttpClient()
.newBuilder()
.addInterceptor(httpLoggingInterceptor)
.build();
retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl("https://randomuser.me/")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
populateUsers();
}
private void initViews() {
recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
}
private void populateUsers() {
Call<RandomUsers> randomUsersCall = getRandomUserService().getRandomUsers(10);
randomUsersCall.enqueue(new Callback<RandomUsers>() {
@Override
public void onResponse(Call<RandomUsers> call, @NonNull Response<RandomUsers> response) {
if(response.isSuccessful()) {
mAdapter = new RandomUserAdapter();
mAdapter.setItems(response.body().getResults());
recyclerView.setAdapter(mAdapter);
}
}
@Override
public void onFailure(Call<RandomUsers> call, Throwable t) {
Timber.i(t.getMessage());
}
});
}
public RandomUsersApi getRandomUserService(){
return retrofit.create(RandomUsersApi.class);
}
}
-
위 코드는 두 가지 문제가 두드러진다.
1. init 과정이 깔끔하지 못해 보인다.
2. Testability 가 떨어진다. 예를 들면 Picasso 도 constructor 로 inject 되는 것이 좋다.
-
많은 사람들이 DI 와 Dagger2 를 어려워하는데 그 이유는 무언가가 빠졌기 때문이다.
조각과 node 를 연결해주는 것이 빠졌다.
이 빠진 것(혹은 비밀)은 dependency graph 이다.
-
Dependency Graph 는 단순한 diagram 으로 class 간의 dependency 를 화살표와 라인으로 마킹한 것이다.
DI graph 에서 중요한 것은 dependency graph 의 꼭지이다. (참조를 하기만 하는 놈, 혹은 interface 로 사용되는 녀석)
이 친구들만 component 에서 잘 제공해주면 된다.
그리고 Module 에서, 그 안의 Provider 에서는 dependency 에 있는 녀석들을 param 으로 받아서 전달해주면 된다.
-
상호간의 dependency 있는 module 간은 @Module annotation 에 includes 를 사용해주면 된다.
ex)
@Module(includes = OkHttpCLientModule.class)
-
Module 사용처(연결자)인 Component 에서는 @Component annotation 에 modules 를 사용해주면 된다.
ex)
@Comoponent(modules = )
-
@Scope annotation 은 dagger 에게 single instance 를 만들 것을 알려준다.
DaggerComponent.build() 를 여러번 호출해도 singleton 으로 작동한다.
우리가 해줄 것은 다음과 같이 annotation 을 만들어서 적용하는 것이다.
@Scope
@Retention(RetentionPolicy.CLASS)
public @interface RandomuserApplicationScope{ }
@RandomUserApplicationScope
@Component(modules = )
public interface RandomUserComponent{ … }
@Module(includes = OkHttpClientModule.class)
public class RandomUserModule{
@RandomUserApplicationScope
@Provides
public Retrofit retrofit(OkHttpClient okHttpClient, GsonConverterFactory gsonConverterFactory, Gson gson){
return new Retrofit. ...
}
}
-
안드로이드에서 주로 쓰는 Context 는 ApplicationContext 와 Activity context 가 있다.
ApplicationContext 의 경우 ContextModule 이라는 녀석을 사용해서 provide 하면 되지만 ActivityModule 은 없다. 그래서 만들어야 한다.
@Module
public class ActivityModule{
private final Context context;
ActivityModule(Activity context){
this.context = context;
}
@RandomUserApplicationScope
@Provides
public Context context(){
return context;
}
}
-
ActivityModule 을 만들었지만 return 하는 것은 모두 Context 라..
ActivityModule 의 것을 inject 할지 ContextModule 의 것을 inject 할지 알지 못한다.
이 떄 @Named annotation 을 쓰면 된다.
-
ActivityModule 의 context provider 에는 @Named(“activity_context”) 를..
ContextModule 의 context provider 에는 @Named(“application_context”) 를 넣어준다.
그리고 inject 받는 곳에서도 param 앞에 @Named(“application_context”) 와 같이 써준다.
-
@Named 대신 @Qualifier 를 사용할 수도 있다.
@Qualifier
public @interface ApplicationContext { }
그리고 ContextModule 의 context provider 에 @ApplicationContext 를 넣어준다.
Inject 를 받는 param 에 @ApplicationContext 를 마킹해준다.
-
Activity level 에 dependency 를 만드는 경우.. lifecycle 에 따라 dependency 도 제거되길 바랄 수 있다. 그리고 Application 은 singleton 처럼 사용되길 바랄 것이다.
이렇게 lifecycle 이 다른 곳에 DI 를 하고 싶을 때는 각각의 component 에 대해 각각의 module 을 만드는 것이 추천된다.
-
먼저 @Scope 를 이용해 각각의 scope 를 지정해준다.
@interface MainActivityScope 와 @interface ApplicationScope
-
@Component(dependencies = RandomUserComponent.class)
위와 같이 명시한 것은 dagger 에게 추가적인 dependency 가 필요할 경우 RandomUserComponent 를 참조하라고 이야기하는 것이다.
-
@Component 에 inject method 를 만들어 injection 을 의도적으로 주입받는 방법이 있다.
@Component(modules = MainActivityModule.class, dependencies = RandomUserComponent.class)
@MainActivityScope
public interface MainActivityComponent {
void injectMainActivity(MainActivity mainActivity);
}
MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
.mainActivityModule(new MainActivityModule(this))
.randomUserComponent(RandomUserApplication.get(this).getRandomUserApplicationComponent())
.build();
mainActivityComponent.injectMainActivity(this);
-
그래서!! 최종 코드는 여기를 보자.
-
참고 자료 : https://medium.com/@harivigneshjayapalan/dagger-2-for-android-beginners-introduction-be6580cb3edb
@, @ Componnet, @ Module, @ Named, @ Provides, @Component modules, @Inject, @Module includes, @Qualifier, @scope, Advanced Dagger2, annotation processor, compile time dependency injection framework, constructor injection, ContextModule, CREATE, dagger, dagger2, Dagger2 annotation, Dagger2 for Android Beginners, dependency graph, dependency injection, di 의 비밀, di 장점, field injection, inject method, inversion of control, Method Injection, Reflection, static
'프로그래밍 놀이터 > 안드로이드, Java' 카테고리의 다른 글
[android] Dagger2 tutorial part2 (0) | 2018.12.05 |
---|---|
[android] Dagger2 Tutorial (0) | 2018.12.04 |
[android] Oreo 에서는 Wakelock 이 소용 없다?! (0) | 2018.12.02 |
[android] WakefulBroadcastReceiver 를 알아보자! (2) | 2018.12.01 |
[android] NoClassDefFoundError for SafeBrowsingResponse (0) | 2018.11.30 |
댓글