Skip to content

Commit 778cfe3

Browse files
committed
update README
1 parent 8578c34 commit 778cfe3

File tree

2 files changed

+319
-0
lines changed

2 files changed

+319
-0
lines changed

README.md

Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
<img src="https://github.com/YukiMatsumura/AndroidDaggerSample/blob/master/art/android_robot.png?raw=true" align="right" />
2+
3+
# DAGGER 2.11-rc1 SAMPLE
4+
5+
### 🗡 Dagger 2.11
6+
7+
Dagger2.10で[`dagger.android`モジュールがリリース](https://google.github.io/dagger/android)されました.
8+
本稿ではDagger2.10と2.11でリリースされた`dagger.android`モジュールの使い方について簡単に紹介したいと思います.
9+
10+
本題へ入る前に, Dagger2.11では当然, 歴代のバージョンで追加されてきた機能を土台にしています.
11+
Daggerを触ったことがない人は [Android: Dagger2](http://yuki312.blogspot.jp/2016/03/android-dagger2.html) を.
12+
Subcomponentを使ったことがない人は[Android: Dagger2 - Subcomponent vs. dependencies](http://yuki312.blogspot.jp/2016/02/android-dagger2-subcomponent-vs.html)を.
13+
マルチバインディングを使ったことがない人は[Dagger2. MultibindingでComponentを綺麗に仕上げる](http://yuki312.blogspot.jp/2017/02/dagger2-multibindingcomponent.html)を一度読んでから本稿に戻ってくると理解しやすいと思います.
14+
15+
Dagger(Dependency Injection)を最大限に活かせるのは, 依存オブジェクトをDagger自身が生成して, 依存性を満たすようにデザインすることでしょう. しかし, AndroidはActivityやFragmentといったOSが生成・管理するオブジェクトがあり, Daggerが全てを生成・管理することができません.
16+
そうした場合, 次のようにフィールドインジェクションを使って依存性を満たすことになります.
17+
18+
```java
19+
public class MainActivity extends Activity {
20+
@Inject Hoge hoge;
21+
22+
@Override
23+
public void onCreate(Bundle savedInstanceState) {
24+
super.onCreate(savedInstanceState);
25+
// 必ず最初に実行すること!
26+
((App) getContext().getApplicationContext())
27+
.getApplicationComponent()
28+
.newActivityComponentBuilder()
29+
.activity(this)
30+
.build()
31+
.inject(this);
32+
// ...
33+
}
34+
}
35+
```
36+
37+
これにはいくつかの問題があります.
38+
39+
1. まず, ActivityやFragment, Service, ContentProviderといったOS管理のクラスへインジェクションする数だけコピペコードが出来上がり, メンテナンス性を悪くします.
40+
2. そしてなにより, クラスが依存性を注入するオブジェクト(ComponentやModules)のことについてそれぞれのクラスが知っている必要があるため, Dependency Injectionのコア原則を破っています.
41+
42+
今回紹介する`dagger.android`モジュールを導入すると, これらの問題を解決することができます.
43+
44+
```
45+
NOTE:
46+
android.daggerモジュールはまだBetaバージョンのため今後変更される可能性があります.
47+
今でもクラス名がリネームされるなどしているため, 他でコードを参考にされる場合はdaggerのバージョンに注意する必要があります.
48+
49+
本稿では現時点で最新のリリースバージョンDagger2.11-rc1を対象にしています.
50+
StableのDagger2.10からの変更点もありますので, Dagger2.10を使う場合は変更点にご注意ください.
51+
52+
Dagger2.10 -> 2.11の変更点:
53+
- New API: @ContributesAndroidInjector simplifies the usage of dagger.android
54+
- All HasDispatching*Injectors are renamed to Has*Injector. They also return an AndroidInjector instead of a DispatchingAndroidInjector
55+
- Added DaggerApplication and DaggerContentProvider
56+
57+
リネーム情報はGitHubのリリースページに記載されています.
58+
https://github.com/google/dagger/releases
59+
```
60+
61+
62+
### 依存ライブラリの追加
63+
64+
まずはDagger2.11のライブラリを追加しないとはじまりません.
65+
build.gradleのdependenciesに次のライブラリを追加します.
66+
67+
```
68+
// Core dependencies
69+
compile 'com.google.dagger:dagger:2.11-rc1'
70+
annotationProcessor 'com.google.dagger:dagger-compiler:2.11-rc1'
71+
72+
// Android dependencies
73+
compile 'com.google.dagger:dagger-android:2.11-rc1'
74+
annotationProcessor 'com.google.dagger:dagger-android-processor:2.11-rc1'
75+
76+
// Require if use android support libs.
77+
compile 'com.google.dagger:dagger-android-support:2.11-rc1'
78+
```
79+
80+
`dagger-android-*`なモジュールがDaggerのAndroid拡張です.
81+
プロジェクトでサポートライブラリを使用している場合は`dagger-android-support`も必要です.
82+
83+
余談ですが, 手元の環境ではfindbugsのdependencyでコンフリクトが起きたので, 合わせて解消しています.
84+
85+
```
86+
エラー:
87+
Error:Conflict with dependency 'com.google.code.findbugs:jsr305' in project ':app'. Resolved versions for app (3.0.1) and test app (2.0.1) differ. See http://g.co/androidstudio/app-test-app-conflict for details.
88+
89+
解決: espresso-coreの依存モジュールからjsr305をexcludeしておく
90+
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
91+
exclude group: 'com.android.support', module: 'support-annotations'
92+
exclude group: 'com.google.code.findbugs', module: 'jsr305'
93+
})
94+
```
95+
96+
Daggerのライブラリを取得したらComponent, Moduleを作成していきましょう.
97+
98+
99+
100+
### ActivityのComponent/Module作成
101+
102+
順を追って必要なオブジェクトを作って行きます. まずはMainActivityに紐づくMainComponentの定義から.
103+
104+
MainComponentはこのあと作るアプリケーションコンポーネントのサブコンポーネントとして定義するので`@Subcomponent`アノテーションをつけます.
105+
さらに, コンポーネントビルダー`@Subcomponent.Builder`を同じく宣言します.
106+
107+
```
108+
package com.yuki312.androiddaggersample;
109+
110+
import dagger.Subcomponent;
111+
import dagger.android.AndroidInjector;
112+
113+
@Subcomponent
114+
public interface MainComponent extends AndroidInjector<MainActivity> {
115+
@Subcomponent.Builder
116+
abstract class Builder extends AndroidInjector.Builder<MainActivity> {
117+
}
118+
}
119+
```
120+
121+
MainComponentには`AndroidInjector`インタフェースを継承させます.
122+
`AndroidInjector`はAndroidのコアコンポーネント(Activity, Fragment, Service, BroadcastReceiver, ContentProvider)に依存性を注入するメソッド`inject(T)`を定義したインタフェースです.
123+
124+
このインタフェースは■■■
125+
126+
次にMainModuleを定義します.
127+
128+
```
129+
import android.app.Activity;
130+
import dagger.Binds;
131+
import dagger.Module;
132+
import dagger.android.ActivityKey;
133+
import dagger.android.AndroidInjector;
134+
import dagger.multibindings.IntoMap;
135+
136+
@Module
137+
public abstract class MainModule {
138+
139+
@Binds @IntoMap @ActivityKey(MainActivity.class)
140+
abstract AndroidInjector.Factory<? extends Activity> bindInjectorFactory(
141+
MainComponent.Builder builder);
142+
}
143+
```
144+
145+
146+
続いてアプリケーションクラス用のAppModule.
147+
148+
```java
149+
package com.yuki312.androiddaggersample;
150+
151+
import dagger.Module;
152+
153+
@Module(subcomponents = { MainComponent.class })
154+
public class AppModule {
155+
}
156+
```
157+
158+
続いてAppComponent
159+
160+
```java
161+
package com.yuki312.androiddaggersample;
162+
163+
import dagger.Component;
164+
import dagger.android.AndroidInjector;
165+
import dagger.android.support.AndroidSupportInjectionModule;
166+
167+
@Component(modules = { AndroidSupportInjectionModule.class, AppModule.class, MainModule.class })
168+
public interface AppComponent extends AndroidInjector<App> {
169+
170+
@Component.Builder
171+
abstract class Builder extends AndroidInjector.Builder<App> {
172+
}
173+
}
174+
```
175+
176+
`modules={...}`にはインジェクションモジュールを含める必要があります.
177+
インジェクションモジュールには次の種類が用意されています.
178+
179+
- `AndroidInjectionModule.class`(サポートライブラリを使わない場合)
180+
- `AndroidSupportInjectionModule.class`(サポートライブラリを使う場合)
181+
182+
インジェクションモジュールには, AndroidのコアコンポーネントにinjectするComponent/SubComponentのファクトリクラスである`AndroidInjector.Factory`を値に持つMapがAndroidコアコンポーネント毎に定義されており, それぞれのインスタンスはマルチバイインディングの仕組みで構築されています.
183+
184+
```
185+
@Module
186+
public abstract class AndroidInjectionModule {
187+
@Multibinds
188+
abstract Map<Class<? extends Activity>, AndroidInjector.Factory<? extends Activity>>
189+
activityInjectorFactories();
190+
191+
@Multibinds
192+
abstract Map<Class<? extends Fragment>, AndroidInjector.Factory<? extends Fragment>>
193+
fragmentInjectorFactories();
194+
195+
@Multibinds
196+
abstract Map<Class<? extends Service>, AndroidInjector.Factory<? extends Service>>
197+
serviceInjectorFactories();
198+
...
199+
```
200+
201+
`AndroidInjectionModule`, `AndroidSupportInjectionModule``AndroidInjector.Factory`の管理に必要であることがわかります.
202+
アプリケーション全体に渡るコアコンポーネントを管理するため, 基本的にはApplicationスコープのコンポーネントで管理することになります.
203+
AppComponentにはビルダー`AndroidInjector.Builder`も忘れずに定義しておきます.
204+
205+
206+
207+
### DaggerApplication
208+
209+
次にApplicationクラスの定義です.
210+
Applicationクラスには各Androidコアコンポーネント用の`AndroidInjector`を定義する必要があります.
211+
`AndroidInjector`はActivityやFragmentといったコアコンポーネントに依存性を注入するためのインジェクター用のインタフェースです.
212+
コアコンポーネント用のインジェクターには次のものがあります.
213+
214+
- HasActivityInjector
215+
- HasFragmentInjector,
216+
- HasServiceInjector,
217+
- HasBroadcastReceiverInjector,
218+
- HasContentProviderInjector
219+
- HasSupportFragmentInjector(dagger-android-support)
220+
221+
それぞれのインタフェースには各コアコンポーネント専用のインジェクターを返すメソッドが定義されているわけですが, Applicationクラスでこれら全てのインジェクターを実装するのは面倒なので, Dagger2.11では`DaggerApplication`クラスが提供されました.
222+
223+
- dagger.android.DaggerApplication(サポートライブラリを使わない場合)
224+
- dagger.android.support.DaggerApplication(サポートライブラリを使う場合)
225+
226+
Dagger2.11-rc1ではサポートライブラリ対応/非対応でクラス名が同じなのでextendsする際には注意が必要です.
227+
また, DaggerApplicationはApplication用のインジェクターを返す`applicationInjector`をabstractメソッドとして定義してあるので, これをオーバーライドしておきます.
228+
これで, Applicationクラスへのフィールドインジェクションもサポートされます.
229+
230+
```java
231+
package com.yuki312.androiddaggersample;
232+
233+
import dagger.android.AndroidInjector;
234+
import dagger.android.support.DaggerApplication;
235+
236+
public class App extends DaggerApplication {
237+
238+
@Override protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
239+
return DaggerAppComponent.builder().create(this);
240+
}
241+
}
242+
```
243+
244+
245+
### 仕上げ
246+
247+
最後の仕上げにMainActivityでフィールドインジェクションを実装しましょう.
248+
249+
```java
250+
package com.yuki312.androiddaggersample;
251+
252+
...
253+
import dagger.android.AndroidInjection;
254+
255+
public class MainActivity extends AppCompatActivity {
256+
257+
...
258+
259+
@Override protected void onCreate(Bundle savedInstanceState) {
260+
AndroidInjection.inject(this);
261+
super.onCreate(savedInstanceState);
262+
...
263+
}
264+
}
265+
```
266+
267+
`AndroidInjection.inject(this);`. たったこれだけです! 簡単ですね:)
268+
従来のComponentやModuleの指定が現れないのでDependency Injectionの原則にも忠実です.
269+
270+
271+
272+
### おまけ
273+
274+
#### dagger-android-support は何者か
275+
276+
`dagger.android`の肝はAndroidコアコンポーネントへのインジェクションサポートです.
277+
今回登場した `HasSupportFragmentInjector`, `AndroidSupportInjectionModule`, `dagger.android.support.DaggerApplication`が主にサポートライブラリ向けのクラスになります.
278+
これらの中身を覗くと, `android.support.v4.app.Fragment`のためのバインディングマップであったり, インジェクターであったりの処理が定義されています.
279+
つまり, サポートライブラリのFragmentを使ったinjectionをサポートするためにこれらのライブラリが必要になってきます.
280+
サポートライブラリのFragmentを使わないのであれば必ずしも必要というわけではなさそうですね.
281+
282+
283+
#### コアコンポーネントのInjectorはどうやって選ばれる?
284+
285+
ActivityやFragmentといったコアコンポーネントのインジェクターは`AndroidInjectionModule`に定義された`AndroidInjector.Factory`から生成することができますが, これが設定されているマルチバインディングで構築されたMapからファクトリインスタンスを取り出す操作は`DispatchingAndroidInjector`が行なっています.
286+
`DispatchingAndroidInjector`はDaggerが生成するオブジェクトであるためアプリケーション側から直接触ることはないと思いますが, `dagger.android`の内部動作を把握するには押さえておく必要のあるクラスです.
287+
288+
289+
#### ContentProviderInjectorとApplicationInjector
290+
291+
Androidの仕組み上, アプリケーションプロセスがCygoteからforkされて開始される際, ContentProviderの初期化はApplicationの初期化より早いです.
292+
つまり, ActivityやBroadcastReceiver, Serviceなど他のコアコンポーネントと唯一異なってContentProviderのonCreate時にはまだApplicationクラスが初期化(onCreate)されていない可能性があります.
293+
DaggerApplicationクラスを覗くとこの辺りをどう解決しているのかをうかがい知ることができます.
294+
295+
```java
296+
// injectIfNecessary is called here but not on the other *Injector() methods because it is the
297+
// only one that should be called (in AndroidInjection.inject(ContentProvider)) before
298+
// Application.onCreate()
299+
@Override
300+
public AndroidInjector<ContentProvider> contentProviderInjector() {
301+
...
302+
303+
304+
/**
305+
* Lazily injects the {@link DaggerApplication}'s members. Injection cannot be performed in {@link
306+
* Application#onCreate()} since {@link android.content.ContentProvider}s' {@link
307+
* android.content.ContentProvider#onCreate() onCreate()} method will be called first and might
308+
* need injected members on the application. Injection is not performed in the the constructor, as
309+
* that may result in members-injection methods being called before the constructor has completed,
310+
* allowing for a partially-constructed instance to escape.
311+
*/
312+
private void injectIfNecessary() {
313+
if (needToInject) {
314+
```
315+
316+
317+
この他にも, コアコンポーネントのComponent/Module定義を簡略化できる`@ContributesAndroidInjector`や, コアコンポーネントインスタンスをパラメータにとるProviderメソッドの提供方法などもありますが, 本稿では割愛します.
318+
319+
ひとまず, `dagger.android`パッケージがどのようなものになる予定なのか, 本稿で大まかにでも掴めたようでしたら幸いです.

art/android_robot.png

3.98 KB
Loading

0 commit comments

Comments
 (0)