Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Android DI
Search
Daichi Furiya (Wasabeef)
September 16, 2014
Programming
9
1.3k
Android DI
AndroidでDIをしてみる
ButterKnife, Dagger
Daichi Furiya (Wasabeef)
September 16, 2014
Tweet
Share
More Decks by Daichi Furiya (Wasabeef)
See All by Daichi Furiya (Wasabeef)
DevFest Tokyo 2025 - Flutter のアプリアーキテクチャ現在地点
wasabeef
5
2k
About Flutter Architecture
wasabeef
1
250
2023 Flutter/Dart Summary
wasabeef
0
80
I/O Extended 2023 - Dart と Flutter の新機能
wasabeef
0
180
I/O Extended 2023 - Flutter 活用事例
wasabeef
10
3k
What it Takes to be a Flutter Developer
wasabeef
0
200
FlutterKaigi 2022 Keynote
wasabeef
1
610
Flutter Hooks を使ったアプリ開発 / App Development with the Flutter Hooks
wasabeef
2
1.4k
Flutter 2021 の振り返りと今後のアプリ開発に向けて / Looking back on Flutter 2021 and for future app development.
wasabeef
4
2.2k
Other Decks in Programming
See All in Programming
Constant integer division faster than compiler-generated code
herumi
2
590
Strands Agents で実現する名刺解析アーキテクチャ
omiya0555
1
120
あまり知られていない MCP 仕様たち / MCP specifications that aren’t widely known
ktr_0731
0
240
MCPで実現できる、Webサービス利用体験について
syumai
7
2.5k
kiroでゲームを作ってみた
iriikeita
0
150
サイトを作ったらNFCタグキーホルダーを爆速で作れ!
yuukis
0
180
AIのメモリー
watany
13
1.4k
バイブスあるコーディングで ~PHP~ 便利ツールをつくるプラクティス
uzulla
1
330
AHC051解法紹介
eijirou
0
420
Android 15以上でPDFのテキスト検索を爆速開発!
tonionagauzzi
0
200
GitHub Copilotの全体像と活用のヒント AI駆動開発の最初の一歩
74th
7
2.5k
Scale out your Claude Code ~自社専用Agentで10xする開発プロセス~
yukukotani
9
1.9k
Featured
See All Featured
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
183
54k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
229
22k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
34
6k
Designing for Performance
lara
610
69k
Measuring & Analyzing Core Web Vitals
bluesmoon
8
550
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
10
1k
The Language of Interfaces
destraynor
158
25k
Rebuilding a faster, lazier Slack
samanthasiow
83
9.1k
jQuery: Nuts, Bolts and Bling
dougneiner
63
7.8k
Visualization
eitanlees
146
16k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
358
30k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
332
22k
Transcript
Android Dependency Injection CyberAgent, Inc. ߱ େ
About Me Daichi Furiya @wasabeef_jp wasabeef http://wasabeef.jp
ݱࡏɺϑϧωΠςΟϒͷ ৽نΞϓϦΛ։ൃத
// Google compile 'com.android.support:support-v4:20.+' compile 'com.android.support:support-v13:20.+' compile 'com.android.support:recyclerview-v7:21.0.0-rc1' compile 'com.android.support:cardview-v7:21.0.0-rc1'
compile 'com.google.android.gms:play-services:5.+' // Square compile 'com.squareup.retrofit:retrofit:1.6.+' compile 'com.squareup.okhttp:okhttp:2.0.+' compile ‘com.squareup.okhttp:okhttp-urlconnection:2.0.+’ compile 'com.squareup:otto:1.3.+' compile 'com.jakewharton.timber:timber:2.4.+' compile 'com.jakewharton:butterknife:5.1.+' compile 'com.squareup.dagger:dagger:1.2.+' compile 'com.squareup.dagger:dagger-compiler:1.2.+' // Image Loader compile 'com.github.bumptech.glide:glide:3.3.+'.2.+' // JSON compile 'com.fasterxml.jackson.core:jackson-core:2.2.+' compile 'com.fasterxml.jackson.core:jackson-databind:2.2.+' // UI compile 'com.github.ksoichiro:simplealertdialog:1.1.1@aar' compile 'com.commonsware.cwac:richedit:0.3.0' compile 'com.github.manuelpeinado.fadingactionbar:fadingactionbar:3.1.+' // Serialize compile 'org.parceler:parceler-api:0.2.+' provided 'org.parceler:parceler:0.2.+' compile 'com.github.satyan:sugar:1.4@aar' // Unit Test testCompile 'junit:junit:4.10' testCompile 'org.robolectric:robolectric:2.+' testCompile 'org.assertj:assertj-core:1.6.1'
// Google compile 'com.android.support:support-v4:20.+' compile 'com.android.support:support-v13:20.+' compile 'com.android.support:recyclerview-v7:21.0.0-rc1' compile 'com.android.support:cardview-v7:21.0.0-rc1'
compile 'com.google.android.gms:play-services:5.+' // Square compile 'com.squareup.retrofit:retrofit:1.6.+' compile 'com.squareup.okhttp:okhttp:2.0.+' compile ‘com.squareup.okhttp:okhttp-urlconnection:2.0.+’ compile 'com.squareup:otto:1.3.+' compile 'com.jakewharton.timber:timber:2.4.+' compile 'com.jakewharton:butterknife:5.1.+' compile 'com.squareup.dagger:dagger:1.2.+' compile 'com.squareup.dagger:dagger-compiler:1.2.+' // Image Loader compile 'com.github.bumptech.glide:glide:3.3.+'.2.+' // JSON compile 'com.fasterxml.jackson.core:jackson-core:2.2.+' compile 'com.fasterxml.jackson.core:jackson-databind:2.2.+' // UI compile 'com.github.ksoichiro:simplealertdialog:1.1.1@aar' compile 'com.commonsware.cwac:richedit:0.3.0' compile 'com.github.manuelpeinado.fadingactionbar:fadingactionbar:3.1.+' // Serialize compile 'org.parceler:parceler-api:0.2.+' provided 'org.parceler:parceler:0.2.+' compile 'com.github.satyan:sugar:1.4@aar' // Unit Test testCompile 'junit:junit:4.10' testCompile 'org.robolectric:robolectric:2.+' testCompile 'org.assertj:assertj-core:1.6.1'
DI Libraries • Google/Guice • RoboGuice • AndroidAnnotations • Dagger
• Butter Knife
Butter Knife
ViewपΓͷ͓ܾ·ΓCodeΛ AnnotationΛར༻ͯ͠ੜ͢Δ View "injection" library for Android which uses annotation
processing to generate boilerplate code for you.
View Injection
public class ExampleActivity extends Activity { TextView mTitle; TextView mBody;
TextView mFooter; ! @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ! mTitle = (TextView) findViewById(R.id.title); mSubTitle = (TextView) findViewById(R.id.body); mFooter = (TextView) findViewById(R.id.footer); } }
public class ExampleActivity extends Activity { @InjectView(R.id.title) TextView mTitle; @InjectView(R.id.subtitle)
TextView mSubtitle; @InjectView(R.id.footer) TextView mFooter; ! @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ! mTitle = (TextView) findViewById(R.id.title); mSubTitle = (TextView) findViewById(R.id.body); mFooter = (TextView) findViewById(R.id.footer); } }
public class ExampleActivity extends Activity { @InjectView(R.id.title) TextView mTitle; @InjectView(R.id.subtitle)
TextView mSubtitle; @InjectView(R.id.footer) TextView mFooter; ! @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ! ButterKnife.inject(this); ! ! } }
View Lists Injection @InjectViews({R.id.first_name, R.id.middle_name, R.id.last_name }) List<EditText> mNameViews;
ButterKnife.apply(mNameViews, View.ALPHA, 0); ButterKnife.apply
static final Action<View> DISABLE = new Action<>() { @Override public
void apply(View view, int index) { view.setEnabled(false); } } ! ! ButterKnife.apply(mNameViews, DISABLE); Custom Action
Listener Injection
public class ExampleActivity extends Activity implements View.OnClickListener { ! @Override
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ! findViewById(R.id.button).setOnClickListener(this); } ! @Override public void onClick(View v) { Button button = (Button) v; button.setText("clicked."); } }
public class ExampleActivity extends Activity { ! ! @Override public
void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ! ! } ! @OnClick(R.id.submit) public void onClickSubmit(Button button) { button.setText(“clicked."); ! } }
public class ExampleActivity extends Activity { ! ! @Override public
void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ! ButterKnife.inject(this); } ! @OnClick(R.id.submit) public void onClickSubmit(Button button) { button.setText(“clicked."); ! } }
Multiple OnClick @OnClick({ R.id.door1, R.id.door2, R.id.door3 }) public void pickDoor(DoorView
door) { // TODO Use.. }
Android Studio Plugin ! -> android-butterknife-zelezny
ButterKnife ·ͱΊ • ViewͷInjectionʹಛԽͯ͠Δ • ػೳ͕গͳ͍͚ͩʹɺγϯϓϧͰ͍͍͢
Square/Dagger
Guice ʁ • ͱͯීٴͯ͠Δ • ඪ४తͳInjectionͷػೳΛඋ͑ͯΔ • ॳظԽٴͼInjection͕͘ɺϝϞϦͷى ͖͍͢
Dagger (ObjectGraph) • ίϯύΠϧ࣌ݕূɿϞδϡʔϧͱೖΛݕূ ͢ΔΞϊςʔγϣϯϓϩηοα • ࣮ߦ࣌ʹɺϦϑϨΫγϣϯΛར༻͠ͳ͍ • ϝϞϦͷӨڹۃΘ͔ͣʹ͢Δ •
ViewͷInjectग़དྷͳ͍
Dagger ͷߏཁૉ
@Module + @Provides ґଘؔΛఏڙ͢ΔͨΊͷΈɾઃఆ
ObjectGraph ґଘؔཧͱΠϯδΣΫλ
@Inject ґଘؔΛཁٻ͢ΔͨΊͷΈ
@Module + @Provides ͷఆٛ
! public class ApiModule { ! public RestAdapter provideRestAdapter(OkHttpClient client)
{ return new RestAdapter.Builder() .setEndpoint(“http://wasabeef.jp”) .setClient(new OkClient(client)) .build(); } ! ! public OkHttpClient provideOkHttpClient() { return new OkHttpClient(); } }
@Module( injects = ExampleActivity.java ) public class ApiModule { !
public RestAdapter provideRestAdapter(OkHttpClient client) { return new RestAdapter.Builder() .setEndpoint(“http://wasabeef.jp”) .setClient(new OkClient(client)) .build(); } ! ! public OkHttpClient provideOkHttpClient() { return new OkHttpClient(); } }
@Module( injects = ExampleActivity.java ) public class ApiModule { @Provides
@Singleton public RestAdapter provideRestAdapter(OkHttpClient client) { return new RestAdapter.Builder() .setEndpoint(“http://wasabeef.jp”) .setClient(new OkClient(client)) .build(); } ! @Provides @Singleton public OkHttpClient provideOkHttpClient() { return new OkHttpClient(); } }
ObjectGraph + @Inject ͷఆٛ
public class ExampleActivity extends Activity { ! ! RestAdapter mRestAdapter;
! @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ! mRestAdapter = new RestAdapter.Builder() .setEndpoint(“http://wasabeef.jp”) .setClient(new OkClient(new OkHttpClient())) .build(); } }
public class ExampleActivity extends Activity { private ObjectGraph mObjectGraph; !
RestAdapter mRestAdapter; ! @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ! mRestAdapter = new RestAdapter.Builder() .setEndpoint(“http://wasabeef.jp”) .setClient(new OkClient(new OkHttpClient())) .build(); } }
public class ExampleActivity extends Activity { private ObjectGraph mObjectGraph; !
RestAdapter mRestAdapter; ! @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ! mObjectGraph = ObjectGraph.create(new ApiModule()); mObjectGraph.inject(this); ! ! } }
public class ExampleActivity extends Activity { private ObjectGraph mObjectGraph; !
@Inject RestAdapter mRestAdapter; ! @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ! mObjectGraph = ObjectGraph.create(new ApiModule()); mObjectGraph.inject(this); ! ! } }
ObjectGraph.plus
public class SignUpActivity extends Activity { private ObjectGraph mObjectGraph; !
@Inject RestAdapter mRestAdapter; ! @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ! ExampleApp app = (ExampleApp) getApplication(); mObjectGraph = app.getObjectGraph(); mObjectGraph.plus(new SignUpModule()); mObjectGraph.inject(this); } }
ग़དྷͳ͍͜ͱ
! ! ! ! @Module( injects = ExampleActivity.java ) public
class ApiModule { @Provides @Singleton public RestAdapter provideRestAdapter(OkHttpClient client) { return new RestAdapter.Builder() .setEndpoint(“http://wasabeef.jp”) .setClient(new OkClient(client)) .build(); } ! /*****/ }
@Module( injects = { ExampleActivity.java, ExampleLoginActivity.java, ExampleWebActivity.java, ExampleImageActivity.java, ExampleCameraActivity.java }
) public class ApiModule { @Provides @Singleton public RestAdapter provideRestAdapter(OkHttpClient client) { return new RestAdapter.Builder() .setEndpoint(“http://wasabeef.jp”) .setClient(new OkClient(client)) .build(); } }
! ! ! ! @Module( injects = BaseActivity.java ) public
class ApiModule { @Provides @Singleton public RestAdapter provideRestAdapter(OkHttpClient client) { return new RestAdapter.Builder() .setEndpoint(“http://wasabeef.jp”) .setClient(new OkClient(client)) .build(); } ! /*****/ }
Google/Dagger
Square/Daggerͱͷҧ͍ ɾτϨʔαϏϦςΟ ɾ໌֬ͳAPI ɾύϑΥʔϚϯε্
None
DaggerͰԿ͕͍͔ͨ͠ʁ
Testable Android
@RunWith(RobolectricTestRunner.class) public class Test { ! @Inject RestAdapter mRestAdapter; !
@Module( includes = ApiModule.class, injects = { TestActivity.class }, overrides = true, complete = false ) class TestApiModule { @Provides @Singleton public RestAdapter provideRestAdapter(OkHttpClient okHttpClient) { return new RestAdapter.Builder() // .setEndpoint(“http://127.0.0.1:8080”) .setClient(new OkClient(client)) .build(); } ! @Provides @Singleton public Client provideOkHttpClient() { return new LocalJsonClient(Robolectric.application.getApplicationContext()); } }
Android Studio Plugin ! ! -> dagger-intellij-plugin
Dagger ·ͱΊ • ৽ͨʹDI༻ͷઃܭΛਅʹߟ͑Δ • υΩϡϝϯτ͕গͳ͍ • ButterKnife, Retrofitͱซ༻͢Δͷ͕ྑ͍ •
Google/Dagger͕ग़ΔͷͰɺকདྷੑ༗Γ
ͬͺΓΞϊςʔγϣϯͩΑͶ @Parcel @JsonInclude @JsonValue @JsonCreator @Module @Singleton @Provides @Inject @Subscribe
@DrawableRes @StringRes @OnClick @InjectView @IdRes @POST @Path @Body @GET @Query @PUT @Multipart @Nullable @NonNull @Override @SuppressLint @TargetApi
͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ɻ