Skip to content

Commit 4bb963e

Browse files
committed
dagger content added
1 parent 721d000 commit 4bb963e

File tree

1 file changed

+281
-0
lines changed

1 file changed

+281
-0
lines changed

_drafts/2019-05-13-dagger.md

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,284 @@ description: "Biblioteki"
99
version: Dagger 2.21
1010
keywords: "dagger, wstrzykiwanie, zależności, depenendency, injection, di, inject, provides, module, component, android, programowanie, programming"
1111
---
12+
13+
## Wstęp
14+
Proces budowania większości aplikacji niezależnie od środowiska polega na tworzeniu różnego typu obiektów, które często wymagają innych zależności i nierzadko mogą być współdzielone. Ręczna inicjalizacja wszystkich zależności bywa kosztowna, czasochłonna oraz może zwiększać nadmiarowość powtarzającego się kodu. Wzorzec `Wstrzykiwanie zależności` (`Dependency Injection`) jest realizacją paradygmatu `Odwrócenia sterowania` (`Inversion of Control`) i przejmuje odpowiedzialność inicjalizowania obiektów poprzez wstrzykiwanie oczekiwanych zależności w odpowiednie miejsca. Na podstawie zdefiniowanych reguł `Dagger` dokonuje analizy zależności oraz generuje kod odpowiedzalny za wiązanie obiektów, a jego użycie opiera się wyłącznie na wykorzystaniu adnotacji i kontroli kompilacji w celu analizy i weryfikacji zależności. Dagger upraszcza dostęp do współużytkowanych instancji, ułatwia proces testowania i refaktoryzacji oraz jest prosty w konfiguracji.
15+
16+
## Wstrzykiwanie
17+
Obiekt oznaczony adnotacją `@Inject` oczekuje inicjalizacji poprzez wstrzyknięcie zależności z poziomu komponentu. Jeśli kilka instancji tego samego typu ma zostać wstrzyknięte wówczas należy dokonać rozróżnienia za pomocą adnotacji `@Named` o wartości zgodnej z deklaracją w module. Istnieje także możliwość definicji własnego kwalifikatora przy użyciu adnotacji `@Qualifier` w definicji adnotacji.
18+
19+
{% highlight java %}
20+
public class InjectClass {
21+
//just inject
22+
@Inject String dependency;
23+
24+
//the same type so must be recognized by some qualifiers
25+
@Inject @Named("triangle") Dependency dependencyRed;
26+
@Inject @Named("quarter") Dependency dependencyBlue;
27+
@Inject @Type("oval") Dependency dependencyOrange;
28+
}
29+
30+
@Qualifier
31+
@Retention(RUNTIME)
32+
public @interface Type {
33+
//qualifier annotations to use like @Named, for example:
34+
//@Inject @Type("type") SimpleDependency dependency
35+
}
36+
{% endhighlight %}
37+
38+
## Moduł
39+
Klasa oznaczona jako `@Module` staje się modułem, którego zadaniem jest deklaracja i konfiguracja dostarczanych przez niego zależności co odbywa się przy użyciu metod oznaczonych adnotacją `@Provides`. To jaka zależność ma zostać wstrzyknięta rozpoznawane jest przez komponent na podstawie zgodności typów i kwalifikatora dlatego nazwa metody nie ma znaczenia. Jednak dobrą praktyką jest przyjęcie stałego schematu nazewnictwa. Dodatkowo adnotacja `@Singleton` zapewnia, że instancja obiektu będzie singletonem w obrębie zakresu.
40+
41+
{% highlight java %}
42+
//create main app module to provide Application and Context
43+
@Module
44+
public class AppModule {
45+
46+
private Application application;
47+
48+
public AppModule(Application application) {
49+
this.application = application;
50+
}
51+
52+
@Provides
53+
@Singleton
54+
Application providesApplication() {
55+
return application;
56+
}
57+
}
58+
59+
//some manager module
60+
@Module
61+
public class ManagerModule {
62+
63+
private String url;
64+
65+
public ManagerModule(String name) {
66+
this.url = name;
67+
}
68+
69+
@Provides @Named("red")
70+
SimpleDependency provideSimpleDependencyRed() {
71+
SimpleDependency.Builder builder = new SimpleDependency.Builder();
72+
builder.setColor("red").setShape("oval");
73+
return builder.build();
74+
}
75+
76+
@Provides @Named("blue")
77+
SimpleDependency provideSimpleDependencyBlue() {
78+
SimpleDependency.Builder builder = new SimpleDependency.Builder();
79+
builder.setColor("blue").setShape("triangle");
80+
return builder.build();
81+
}
82+
83+
@Provides
84+
@Singleton
85+
ComplexDependency provideComplexDependency(NetworkManager networkDependency) {
86+
return new ComplexDependency(networkDependency);
87+
}
88+
89+
@Provides
90+
@Singleton
91+
NetworkManager provideNetworkDependency() {
92+
return new NetworkManager(url, 80);
93+
}
94+
95+
@Provides
96+
@Singleton
97+
SharedPreferences providesSharedPreferences(Application application) {
98+
return PreferenceManager.getDefaultSharedPreferences(application);
99+
}
100+
}
101+
{% endhighlight %}
102+
103+
## Komponent
104+
Komponent buduje zależności zadeklarowane w modułach oraz umożliwia ich dostarczenie przez zainteresowane klasy. Aby stworzyć komponent należy oznaczyć klasę adnotacją `@Component`, wskazać źródła zależności w postaci listy modułów oraz stworzyć metodę przyjmującą jako argument klasę do której mogą zostać wstrzyknięte zależności. Zachodzi tutaj `silne typowanie` w związku z czym nie możliwe jest deklaracja klasy bazowej jako parametu. Każda aplikacja wymaga posiadania komponentu bazowego, który najlepiej stworzyć w głównej klasie aplikacji. Jeśli któryś z modułów komponentu posiada konstruktor argumentowy wówczas inicjalizacja komponentu zachodzi przy użyciu budowniczego `builder`, a w przeciwnym wypadku wystarczy wywołanie metody `create`.
105+
106+
{% highlight java %}
107+
@Singleton
108+
@Component(modules={AppModule.class, ManagerModule.class})
109+
public interface AppComponent {
110+
111+
void inject(MainActivity activity);
112+
//add more inject methods for activity, fragment or service
113+
// Dagger relies on strongly typed classes so do not pass base class
114+
}
115+
116+
//initialize component
117+
public class App extends Application {
118+
119+
private AppComponent appComponent;
120+
121+
@Override
122+
public void onCreate() {
123+
super.onCreate();
124+
125+
//DaggerAppComponent is generated class with name based on Dagger prefix and component name
126+
appComponent = DaggerAppComponent.builder()
127+
//set list of modules that are included in component
128+
.appModule(new AppModule(this)) //builder method name correspond to module name
129+
.managerModule(new ManagerModule("androidcode.pl"))
130+
.build();
131+
132+
//if modules of component doesn't have any constructor with arguments than this build could be
133+
//appComponent = DaggerAppComponent.create()
134+
}
135+
136+
public AppComponent getAppComponent() {
137+
return appComponent;
138+
}
139+
}
140+
{% endhighlight %}
141+
142+
Wstrzykiwanie zależności oznaczonych jako `@Inject` w klasie docelowej zachodzi poprzez wywołanie metody komponentu (jeśli został on wcześniej zainicjalizowany).
143+
144+
{% highlight java %}
145+
public class MainActivity extends AppCompatActivity {
146+
147+
@Inject @Named("red") SimpleDependency simpleDependencyRed;
148+
@Inject @Named("blue") SimpleDependency simpleDependencyBlue;
149+
@Inject ComplexDependency complexDependency;
150+
@Inject SharedPreferences sharedPreferences;
151+
152+
@Override
153+
protected void onCreate(Bundle savedInstanceState) {
154+
super.onCreate(savedInstanceState);
155+
setContentView(R.layout.activity_main);
156+
157+
((App) getApplication()).getAppComponent()
158+
.inject(this);
159+
160+
//do something with dependencies
161+
simpleDependencyRed.getInfo();
162+
simpleDependencyBlue.getInfo();
163+
complexDependency.getInfo();
164+
sharedPreferences.contains("KEY");
165+
}
166+
}
167+
{% endhighlight %}
168+
169+
## Zakres
170+
Komponenty i moduły w Dagger działają w podanym zakresie (domyślnie w obszarze całej aplikacji) w obrębie którego dostępne są zależności. Rolą zakresu jest informowanie programisty o przeznaczeniu i cyklu życia danej zależności czy komponentu, jest więc swego rodzaju etykietą informacyjną. Aby stworzyć zakres należy zdefiniować adnotację oznaczoną jako `@Scope`.
171+
172+
{% highlight java %}
173+
@Scope
174+
@Retention(value= RetentionPolicy.RUNTIME)
175+
public @interface UserScope {
176+
//informs about custom lifecycle time
177+
//use this annotations in cases like @Singleton
178+
}
179+
{% endhighlight %}
180+
181+
## Podkomponent
182+
Rozwinięcie grafu drzewa może odbywać się przy użyciu podkomponentów, które rozszerzają zależności komponentu bazowego co sprawia, że komponenty te są ze sobą powiązane. Aby stworzyć komponent, który będzie podkomponentem należy oznaczyć go adnotacją @Subcomponent, dodać fabrykę w komponencie bazowym zwracającą podkomponent oraz dokonać inicjalizacji we właściwym zakresie.
183+
184+
{% highlight java %}
185+
@Module
186+
public class UserModule {
187+
188+
private final UserActivity activity;
189+
190+
public UserModule(UserActivity activity) {
191+
this.activity = activity;
192+
}
193+
194+
@Provides @UserScope
195+
public UserManager providesUserDependency() {
196+
return new UserManager("global");
197+
}
198+
}
199+
200+
@UserScope
201+
@Subcomponent(modules=UserModule.class)
202+
public interface UserComponent {
203+
204+
void inject(UserActivity userActivity);
205+
}
206+
207+
@Singleton
208+
@Component(modules={AppModule.class, ManagerModule.class})
209+
public interface AppComponent {
210+
211+
//inject methods
212+
void inject(MainActivity activity);
213+
214+
//for subcomponent
215+
UserComponent userSubcomponent(UserModule module);
216+
}
217+
218+
public class UserActivity extends AppCompatActivity {
219+
220+
@Inject UserManager manager; //from subcomponent
221+
@Inject SharedPreferences sharedPreferences; //from parent component
222+
223+
@Override
224+
protected void onCreate(Bundle savedInstanceState) {
225+
super.onCreate(savedInstanceState);
226+
setContentView(R.layout.activity_user);
227+
228+
((App) getApplication()).getAppComponent()
229+
.userSubcomponent(new UserModule(this))
230+
.inject(this);
231+
232+
//do something with dependencies
233+
manager.getConfiguration();
234+
sharedPreferences.contains("KEY");
235+
}
236+
}
237+
{% endhighlight %}
238+
239+
## Komponent zależny
240+
Rozszerzenie funckjonalności komponentu może zostać zrealizowane za pomocą wskazania zależności w postaci komponentu bazowego w komponencie pochodnym. Innymi słowy komponent rozszerzający jest swego rodzaju delegatem i poza modułami przyjmuje także zależność komponentu, który jest dla niego dostawcą. Komponent bazowy dostarcza `API` w postaci metod zwracających dany oczekiwany typ zależności (podobnie jak robi to moduł).
241+
242+
{% highlight java %}
243+
@UserScope
244+
@Component(dependencies=AppComponent.class, modules=UserModule.class)
245+
public interface UserComponent {
246+
247+
void inject(UserActivity userActivity);
248+
}
249+
250+
@Singleton
251+
@Component(modules={AppModule.class, ManagerModule.class})
252+
public interface AppComponent {
253+
254+
//remove injection methods if child performs this and no need to use only base component
255+
void inject(MainActivity activity);
256+
257+
Application application();
258+
@Named("red") SimpleDependency simpleDependencyRed();
259+
@Named("blue") SimpleDependency simpleDependencyBlue();
260+
ComplexDependency complexDependency();
261+
NetworkManager networkManager();
262+
SharedPreferences sharedPreferences();
263+
}
264+
265+
public class UserActivity extends AppCompatActivity {
266+
267+
@Inject UserManager manager; //from dependent component
268+
@Inject SharedPreferences sharedPreferences; //from parent component
269+
270+
@Override
271+
protected void onCreate(Bundle savedInstanceState) {
272+
super.onCreate(savedInstanceState);
273+
setContentView(R.layout.activity_user);
274+
275+
//get parent component
276+
AppComponent appComponent = ((App) getApplication()).getAppComponent();
277+
278+
//create child component
279+
UserComponent userComponent = DaggerUserComponent.builder()
280+
.appComponent(appComponent)
281+
.userModule(new UserModule(this))
282+
.build();
283+
284+
//inject
285+
userComponent.inject(this);
286+
287+
//do something with dependencies
288+
manager.getConfiguration();
289+
sharedPreferences.contains("KEY");
290+
}
291+
}
292+
{% endhighlight %}

0 commit comments

Comments
 (0)