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
原理から完全理解するDagger Hilt Migration
Search
Keita Kagurazaka
October 20, 2021
Programming
1
1.9k
原理から完全理解するDagger Hilt Migration
DroidKaigi 2021の発表資料です
Keita Kagurazaka
October 20, 2021
Tweet
Share
More Decks by Keita Kagurazaka
See All by Keita Kagurazaka
SELECT FOR UPDATEの話
kkagurazaka
0
410
Mobileアプリのアーキテクチャ設計法
kkagurazaka
2
1.4k
今後のJetpackでAndroid開発はこう変わる!
kkagurazaka
16
6.2k
外部SDKのViewにマスク処理をする方法と罠
kkagurazaka
0
1k
AWAのフルリニューアルを支えたアーキテクチャ
kkagurazaka
1
900
CQRS Architecture on Android
kkagurazaka
7
3k
suspending functionの裏側
kkagurazaka
3
450
coroutinesで非同期ページネーション
kkagurazaka
1
660
async/awaitで快適非同期ライフ
kkagurazaka
4
1.9k
Other Decks in Programming
See All in Programming
プログラミングどうやる? ~テスト駆動開発から学ぶ達人の型~
a_okui
0
190
デミカツ切り抜きで面倒くさいことはPythonにやらせよう
aokswork3
0
180
Your Perfect Project Setup for Angular @BASTA! 2025 in Mainz
manfredsteyer
PRO
0
120
PostgreSQLで手軽にDuckDBを使う!DuckDB&pg_duckdb入門/osk2025-duckdb
takahashiikki
1
240
エンジニアとして高みを目指す、 利益を生み出す設計の考え方 / design-for-profit
minodriven
23
12k
Go Conference 2025: Goで体感するMultipath TCP ― Go 1.24 時代の MPTCP Listener を理解する
takehaya
7
1.6k
CSC305 Lecture 01
javiergs
PRO
1
400
猫と暮らすネットワークカメラ生活🐈 ~Vision frameworkでペットを愛でよう~ / iOSDC Japan 2025
yutailang0119
0
220
Le côté obscur des IA génératives
pascallemerrer
0
120
(Extension DC 2025) Actor境界を越える技術
teamhimeh
1
220
アメ車でサンノゼを走ってきたよ!
s_shimotori
0
140
iOSDC.pdf
chronos2500
2
660
Featured
See All Featured
[RailsConf 2023] Rails as a piece of cake
palkan
57
5.9k
Why Our Code Smells
bkeepers
PRO
339
57k
The Cult of Friendly URLs
andyhume
79
6.6k
Intergalactic Javascript Robots from Outer Space
tanoku
273
27k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
15
1.7k
Bootstrapping a Software Product
garrettdimon
PRO
307
110k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
248
1.3M
The Pragmatic Product Professional
lauravandoore
36
6.9k
Mobile First: as difficult as doing things right
swwweet
224
10k
Design and Strategy: How to Deal with People Who Don’t "Get" Design
morganepeng
132
19k
The Cost Of JavaScript in 2023
addyosmani
53
9k
Code Reviewing Like a Champion
maltzj
525
40k
Transcript
原理から完全理解する Dagger Hilt Migration Keita Kagurazaka
本発表について • 話すこと ◦ 素のDaggerとdagger.android、Hiltの違い ◦ Hiltへの移行方法 ◦ 移行する際によく当たる問題とその解決方法 •
話さないこと / 前提とする知識 ◦ DIとは何か、その必要性 ◦ Daggerの使い方、どんなクラスがあるかなど
Agenda Daggerの捉え方 dagger.androidとHiltの思想 Hiltに段階的に移行する FAQ 01 02 03 04
コンセプト • 「段階的」なHilt移行 ◦ 日々のプロジェクトと並行して進められるように • 原理から理解する ◦ 実際に移行する際に自ら壁を乗り越えられるように
Daggerの捉え方 dagger.androidとHiltの思想 Hiltに段階的に移行する FAQ Agenda 01 02 03 04
Dagger = Component Tree
Dagger = Component Tree
Componentの提供サービス 1. 依存性(インスタンス)の受け渡し 2. 依存性(インスタンス)の同一性保証
Componentの提供サービス 1. 依存性(インスタンス)の受け渡し 2. 依存性(インスタンス)の同一性保証 = 何回受け渡しても同じインスタンス
@Component(modules = [RepositoryModule::class]) interface ApplicationComponent { fun getUserRepository(): UserRepository fun
inject(app: App) } Componentによる依存性の受け渡し
@Component(modules = [RepositoryModule::class]) interface ApplicationComponent { fun getUserRepository(): UserRepository fun
inject(app: App) } Componentによる依存性の受け渡し
@Component(modules = [RepositoryModule::class]) interface ApplicationComponent { fun getUserRepository(): UserRepository fun
inject(app: App) } Componentによる依存性の受け渡し
@Singleton @Component(modules = [ApplicationModule::class]) interface ApplicationComponent { /* 省略 */
} @Module class ApplicationModule { @Singleton @Provides fun providesThirdPartyLibrary(): ThirdPartyLibrary = ThirdPartyLibrary.Builder.build() } Componentによる同一性保証
@Singleton @Component(modules = [ApplicationModule::class]) interface ApplicationComponent { /* 省略 */
} @Module class ApplicationModule { @Singleton @Provides fun providesThirdPartyLibrary(): ThirdPartyLibrary = ThirdPartyLibrary.Builder.build() } Componentによる同一性保証
@Singleton @Component(modules = [ApplicationModule::class]) interface ApplicationComponent { /* 省略 */
} @Module class ApplicationModule { @Singleton @Provides fun providesThirdPartyLibrary(): ThirdPartyLibrary = ThirdPartyLibrary.Builder.build() } Componentによる同一性保証
倉庫預かりオプション ThirdPartyLibrary ください ThirdParty Library 倉庫 ApplicationComponent 以前と同じ ブツです
@Singleton @Component(modules = [ApplicationModule::class]) interface ApplicationComponent { /* 省略 */
} @Module class ApplicationModule { @Singleton @Provides fun providesThirdPartyLibrary(): ThirdPartyLibrary = ThirdPartyLibrary.Builder.build() } Componentによる同一性保証
@Singleton @Component(modules = [ApplicationModule::class]) interface ApplicationComponent { /* 省略 */
} @Module class ApplicationModule { @Provides fun providesThirdPartyLibrary(): ThirdPartyLibrary = ThirdPartyLibrary.Builder.build() } Componentによる同一性保証 毎回作り直し!
Component = 工場 倉庫 受け渡し窓口
Dagger = Component Tree
インスタンスのライフタイム管理 アプリのプロセス終了まで 残ってるよ! ThirdParty Library ApplicationComponentが 消えるまで残ってるよ! 倉庫 ApplicationComponent
インスタンスのライフタイム管理 LoginActivityComponent ApplicationComponent アプリのプロセス終了まで 残ってるよ! LoginActivityが 消えるまで残ってるよ!
インスタンスのライフタイム管理 LoginActivityComponent ApplicationComponent 先輩のインスタンスも こちらで受け渡します!
インスタンスのライフタイム管理 LoginActivityComponent ApplicationComponent 苦しゅうない 先輩のインスタンスも こちらで受け渡します!
インスタンスのライフタイム管理 LoginActivityComponent ApplicationComponent Component Tree
@Scope @Retention(AnnotationRetention.RUNTIME) annotation class ActivityScope @ActivityScope @Subcomponent interface LoginActivityComponent {
fun inject(activity: LoginActivity) @Subcomponent.Factory interface Factory { fun create(): LoginActivityComponent } } SubcomponentによるTree定義
@Scope @Retention(AnnotationRetention.RUNTIME) annotation class ActivityScope @ActivityScope @Subcomponent interface LoginActivityComponent {
fun inject(activity: LoginActivity) @Subcomponent.Factory interface Factory { fun create(): LoginActivityComponent } } SubcomponentによるTree定義
@Module(subcomponents = [LoginActivityComponent::class]) interface ActivityModule @Singleton @Component(modules = [ ApplicationModule::class,
ActivityModule::class ]) interface ApplicationComponent SubcomponentによるTree定義
@Module(subcomponents = [LoginActivityComponent::class]) interface ActivityModule @Singleton @Component(modules = [ ApplicationModule::class,
ActivityModule::class ]) interface ApplicationComponent SubcomponentによるTree定義
@Module(subcomponents = [LoginActivityComponent::class]) interface ActivityModule @Singleton @Component(modules = [ ApplicationModule::class,
ActivityModule::class ]) interface ApplicationComponent SubcomponentによるTree定義
Component Tree ApplicationComponent ActivityModule LoginActivityComponent ApplicationModule
Component Tree ApplicationComponent ApplicationModule LoginActivityComponent
Component Tree ApplicationC ApplicationM LoginActivityC
Daggerの捉え方 dagger.androidとHiltの思想 Hiltに段階的に移行する FAQ Agenda 01 02 03 04
interface ApplicationComponent { fun loginActivityComponent(): LoginActivityComponent.Factory } class LoginActivity :
AppCompatActivity() { lateinit var component: LoginActivityComponent override fun onCreate(savedInstanceState: Bundle?) { component = (applicationContext as App).component .loginActivityComponent().create() component.inject(this) super.onCreate(savedInstanceState) } } Boilerplate問題
interface ApplicationComponent { fun loginActivityComponent(): LoginActivityComponent.Factory } class LoginActivity :
AppCompatActivity() { lateinit var component: LoginActivityComponent override fun onCreate(savedInstanceState: Bundle?) { component = (applicationContext as App).component .loginActivityComponent().create() component.inject(this) super.onCreate(savedInstanceState) } } Boilerplate問題
dagger.android
dagger.android 書くのが大変なら コード生成すればいいじゃない
@Module interface MainActivityModule { @ActivityScope @ContributesAndroidInjector fun contributesMainActivity(): MainActivity }
@Module( includes = [MainActivityModule::class], subcomponents = [LoginActivityComponent::class] ) interface ActivityModule dagger.androidの解法 - コード生成
@Module interface MainActivityModule { @ActivityScope @ContributesAndroidInjector fun contributesMainActivity(): MainActivity }
@Module( includes = [MainActivityModule::class], subcomponents = [LoginActivityComponent::class] ) interface ActivityModule dagger.androidの解法 - コード生成
@Module interface MainActivityModule { @ActivityScope @ContributesAndroidInjector fun contributesMainActivity(): MainActivity }
@Module( includes = [MainActivityModule::class], subcomponents = [LoginActivityComponent::class] ) interface ActivityModule dagger.androidの解法 - コード生成 MainActivitySubC
@Module interface MainActivityModule { @ActivityScope @ContributesAndroidInjector fun contributesMainActivity(): MainActivity }
@Module( includes = [MainActivityModule::class], subcomponents = [LoginActivityComponent::class] ) interface ActivityModule dagger.androidの解法 - コード生成
@Module interface MainActivityModule { @ActivityScope @ContributesAndroidInjector fun contributesMainActivity(): MainActivity }
@Module( includes = [MainActivityModule::class], subcomponents = [LoginActivityComponent::class] ) interface ActivityModule dagger.androidの解法 - コード生成 MainActivitySubC
@Module interface MainActivityModule { @ActivityScope @ContributesAndroidInjector fun contributesMainActivity(): MainActivity }
@Module( includes = [MainActivityModule::class], subcomponents = [LoginActivityComponent::class] ) interface ActivityModule dagger.androidの解法 - コード生成 ApplicationC MainActivitySubC
@Module(subcomponents = MainActivityModule_ContributesMainActivity.MainActivitySubcomponent.class) public abstract class MainActivityModule_ContributesMainActivity { private MainActivityModule_ContributesMainActivity()
{} @Binds @IntoMap @ClassKey(MainActivity.class) abstract AndroidInjector.Factory<?> bindAndroidInjectorFactory( MainActivitySubcomponent.Factory builder); @Subcomponent @ActivityScope public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> { @Subcomponent.Factory interface Factory extends AndroidInjector.Factory<MainActivity> {} } }
@Module(subcomponents = MainActivityModule_ContributesMainActivity.MainActivitySubcomponent.class) public abstract class MainActivityModule_ContributesMainActivity { private MainActivityModule_ContributesMainActivity()
{} @Binds @IntoMap @ClassKey(MainActivity.class) abstract AndroidInjector.Factory<?> bindAndroidInjectorFactory( MainActivitySubcomponent.Factory builder); @Subcomponent @ActivityScope public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> { @Subcomponent.Factory interface Factory extends AndroidInjector.Factory<MainActivity> {} } }
@Module(subcomponents = MainActivityModule_ContributesMainActivity.MainActivitySubcomponent.class) public abstract class MainActivityModule_ContributesMainActivity { private MainActivityModule_ContributesMainActivity()
{} @Binds @IntoMap @ClassKey(MainActivity.class) abstract AndroidInjector.Factory<?> bindAndroidInjectorFactory( MainActivitySubcomponent.Factory builder); @Subcomponent @ActivityScope public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> { @Subcomponent.Factory interface Factory extends AndroidInjector.Factory<MainActivity> {} } }
@Module(subcomponents = MainActivityModule_ContributesMainActivity.MainActivitySubcomponent.class) public abstract class MainActivityModule_ContributesMainActivity { private MainActivityModule_ContributesMainActivity()
{} @Binds @IntoMap @ClassKey(MainActivity.class) abstract AndroidInjector.Factory<?> bindAndroidInjectorFactory( MainActivitySubcomponent.Factory builder); @Subcomponent @ActivityScope public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> { @Subcomponent.Factory interface Factory extends AndroidInjector.Factory<MainActivity> {} } }
@Module(subcomponents = MainActivityModule_ContributesMainActivity.MainActivitySubcomponent.class) public abstract class MainActivityModule_ContributesMainActivity { private MainActivityModule_ContributesMainActivity()
{} @Binds @IntoMap @ClassKey(MainActivity.class) abstract AndroidInjector.Factory<?> bindAndroidInjectorFactory( MainActivitySubcomponent.Factory builder); @Subcomponent @ActivityScope public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> { @Subcomponent.Factory interface Factory extends AndroidInjector.Factory<MainActivity> {} } } MainActivity
@Module(subcomponents = MainActivityModule_ContributesMainActivity.MainActivitySubcomponent.class) public abstract class MainActivityModule_ContributesMainActivity { private MainActivityModule_ContributesMainActivity()
{} @Binds @IntoMap @ClassKey(MainActivity.class) abstract AndroidInjector.Factory<?> bindAndroidInjectorFactory( MainActivitySubcomponent.Factory builder); @Subcomponent @ActivityScope public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> { @Subcomponent.Factory interface Factory extends AndroidInjector.Factory<MainActivity> {} } } MainActivitySubC MainActivity
@Module(subcomponents = MainActivityModule_ContributesMainActivity.MainActivitySubcomponent.class) public abstract class MainActivityModule_ContributesMainActivity { private MainActivityModule_ContributesMainActivity()
{} @Binds @IntoMap @ClassKey(MainActivity.class) abstract AndroidInjector.Factory<?> bindAndroidInjectorFactory( MainActivitySubcomponent.Factory builder); @Subcomponent @ActivityScope public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> { @Subcomponent.Factory interface Factory extends AndroidInjector.Factory<MainActivity> {} } } MainActivitySubC MainActivity
dagger.androidのinjection
dagger.androidのinjection 1. AndroidInjection.inject
dagger.androidのinjection 1. AndroidInjection.inject DispatchingAndroidInjector
dagger.androidのinjection 1. AndroidInjection.inject DispatchingAndroidInjector ︙ MainActivitySubC MainActivity MainFragmentSubC MainFragment LoginActivitySubC
LoginActivity
dagger.androidのinjection 1. AndroidInjection.inject 2. 渡されたインスタンスのクラスを keyにSubCを見つける DispatchingAndroidInjector ︙ MainActivitySubC MainActivity
MainFragmentSubC MainFragment LoginActivitySubC LoginActivity
dagger.androidのinjection 1. AndroidInjection.inject 2. 渡されたインスタンスのクラスを keyにSubCを見つける 3. SubCをインスタンス化して injection DispatchingAndroidInjector
︙ MainActivitySubC MainActivity MainFragmentSubC MainFragment LoginActivitySubC LoginActivity
class App : Application(), HasAndroidInjector { @Inject lateinit var androidInjector:
DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> = androidInjector } dagger.androidのinjection
dagger.androidのComponent Tree ApplicationC MainActivitySubC MainFragmentSubC HogeFragmentSubC HugaActivitySubC HugaFragmentSubC PiyoFragmentSubC
Hilt
Hilt Componentは こっちで用意するから それ使ってね
https://dagger.dev/hilt/components より引用 Hiltの解法 - プリセットComponent
HiltのComponent Tree (simple ver.) SingletonC ActivityC FragmentC
@InstallIn(SingletonComponent::class) @Module class ApplicationModule { @Singleton @Provides fun providesThirdPartyLibrary(): ThirdPartyLibrary
= ThirdPartyLibrary.Builder.build() } プリセットComponentの使い方
@InstallIn(SingletonComponent::class) @Module class ApplicationModule { @Singleton @Provides fun providesThirdPartyLibrary(): ThirdPartyLibrary
= ThirdPartyLibrary.Builder.build() } プリセットComponentの使い方
@InstallIn(SingletonComponent::class) @Module class ApplicationModule @Singleton @Component(modules = [ApplicationModule::class]) interface ApplicationComponent
プリセットComponentの使い方
@Component(modules = [AllApplicationModule::class]) interface ApplicationComponent { fun inject(instance: NeedInjectionClass) fun
getUserRepository(): UserRepository } インスタンス受け渡し窓口閉鎖のご連絡
@InstallIn(SingletonComponent::class) @EntryPoint interface ApplicationEntryPoint { fun inject(instance: NeedInjectionClass) fun getUserRepository():
UserRepository } EntryPoint
@InstallIn(SingletonComponent::class) @EntryPoint interface ApplicationEntryPoint { fun inject(instance: NeedInjectionClass) fun getUserRepository():
UserRepository } public abstract static class SingletonC implements SingletonComponent, ApplicationEntryPoint { /* 省略 */ } EntryPoint = Componentの1窓口
interface RepositoryProvider { fun getUserRepository(): UserRepository } interface ApplicationInjector {
fun inject(instance: NeedInjectionClass) } @Singleton @Component(modules = [ApplicationModule::class]) interface ApplicationComponent : RepositoryProvider, ApplicationInjector EntryPoint ≈ Componentの分割定義
class MainActivity : AppCompatActivity() class MainFragment : Fragment() HiltのInjection
@AndroidEntryPoint class MainActivity : AppCompatActivity() @AndroidEntryPoint class MainFragment : Fragment()
HiltのInjection
public abstract class Hilt_MainActivity extends AppCompatActivity implements GeneratedComponentManagerHolder { Hilt_MainActivity()
{ super(); _initHiltInternal(); } private void _initHiltInternal() { addOnContextAvailableListener(new OnContextAvailableListener() { @Override public void onContextAvailable(Context context) { inject(); } }); } }
public abstract class Hilt_MainActivity extends AppCompatActivity implements GeneratedComponentManagerHolder { Hilt_MainActivity()
{ super(); _initHiltInternal(); } private void _initHiltInternal() { addOnContextAvailableListener(new OnContextAvailableListener() { @Override public void onContextAvailable(Context context) { inject(); } }); } }
public abstract class Hilt_MainActivity extends AppCompatActivity implements GeneratedComponentManagerHolder { Hilt_MainActivity()
{ super(); _initHiltInternal(); } private void _initHiltInternal() { addOnContextAvailableListener(new OnContextAvailableListener() { @Override public void onContextAvailable(Context context) { inject(); } }); } } ボイラープレート 全部ここでやります
public abstract class Hilt_MainActivity extends AppCompatActivity implements GeneratedComponentManagerHolder { Hilt_MainActivity()
{ super(); _initHiltInternal(); } private void _initHiltInternal() { addOnContextAvailableListener(new OnContextAvailableListener() { @Override public void onContextAvailable(Context context) { inject(); } }); } }
@AndroidEntryPoint class MainActivity : AppCompatActivity() Bytecode transformation in .kt
@AndroidEntryPoint class MainActivity : AppCompatActivity() Bytecode transformation in .kt
@AndroidEntryPoint class MainActivity : Hilt_MainActivity() Bytecode transformation in .dex
@AndroidEntryPoint class MainActivity : AppCompatActivity() @AndroidEntryPoint class MainFragment : Fragment()
HiltのInjection
Daggerの捉え方 dagger.androidとHiltの思想 Hiltに段階的に移行する FAQ Agenda 01 02 03 04
サンプルプロジェクト • https://github.com/k-kagurazaka/hilt-migration • main branch ◦ raw Dagger +
dagger.androidの構成 • hilt-migration branch ◦ Hilt化済み ◦ 各commitはbuild pass
サンプルプロジェクトのComponent Tree ApplicationC MainActivityC MainFragmentC LoginActivityC LoginFragmentC EmailSignupActivityC UserRegistrationFragmentC SnsSignupActivityC
UserRegistrationFragmentC
raw Dagger ApplicationC MainActivityC MainFragmentC LoginActivityC LoginFragmentC EmailSignupActivityC UserRegistrationFragmentC SnsSignupActivityC
UserRegistrationFragmentC
dagger.android ApplicationC MainActivityC MainFragmentC LoginActivityC LoginFragmentC EmailSignupActivityC UserRegistrationFragmentC SnsSignupActivityC UserRegistrationFragmentC
同一Fragment、別Component ApplicationC MainActivityC MainFragmentC LoginActivityC LoginFragmentC EmailSignupActivityC UserRegistrationFragmentC SnsSignupActivityC UserRegistrationFragmentC
移行スタート
移行ステップ 1. gradleの設定 2. Component Treeの接ぎ木 3. Scopeアノテーションの置き換え 4. MainActivityの移行
5. Loginの移行 6. MainFragmentの移行 7. Activityのお掃除 8. dagger.androidの除去 9. ApplicationComponentの削除
移行ステップ 1. gradleの設定 2. Component Treeの接ぎ木 3. Scopeアノテーションの置き換え 4. MainActivityの移行
5. Loginの移行 6. MainFragmentの移行 7. Activityのお掃除 8. dagger.androidの除去 9. ApplicationComponentの削除
SKIP
移行ステップ 1. gradleの設定 2. Component Treeの接ぎ木 3. Scopeアノテーションの置き換え 4. MainActivityの移行
5. Loginの移行 6. MainFragmentの移行 7. Activityのお掃除 8. dagger.androidの除去 9. ApplicationComponentの削除
Component Treeの接ぎ木 ApplicationC MainActivityC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC FragmentC
ApplicationM RepositoryM
Component Treeの接ぎ木 ApplicationC MainActivityC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC FragmentC
ApplicationM RepositoryM
@Singleton @Component(modules = [ AndroidInjectionModule::class, ApplicationModule::class, RepositoryModule::class, ActivityModule::class,]) interface ApplicationComponent
: AndroidInjector<App> { fun loginActivityComponent(): LoginActivityComponent.Factory @Component.Factory interface Factory { fun create(@BindsInstance app: App): ApplicationComponent } } Component Treeの接ぎ木
@Singleton @Component(modules = [ AndroidInjectionModule::class, ApplicationModule::class, RepositoryModule::class, ActivityModule::class,]) interface ApplicationComponent
: AndroidInjector<App> { fun loginActivityComponent(): LoginActivityComponent.Factory @Component.Factory interface Factory { fun create(@BindsInstance app: App): ApplicationComponent } } Component Treeの接ぎ木
@Singleton @Component(modules = [ AndroidInjectionModule::class, ApplicationModule::class, RepositoryModule::class, ActivityModule::class,]) interface ApplicationComponent
: AndroidInjector<App> { fun loginActivityComponent(): LoginActivityComponent.Factory } Component Treeの接ぎ木
@Singleton @Component(modules = [ AndroidInjectionModule::class, ApplicationModule::class, RepositoryModule::class, ActivityModule::class,]) @InstallIn(SingletonComponent::class) @EntryPoint
interface ApplicationComponent : AndroidInjector<App> { fun loginActivityComponent(): LoginActivityComponent.Factory } Component Treeの接ぎ木
@Singleton @Component(modules = [ AndroidInjectionModule::class, ApplicationModule::class, RepositoryModule::class, ActivityModule::class,]) @InstallIn(SingletonComponent::class) @EntryPoint
interface ApplicationComponent : AndroidInjector<App> { fun loginActivityComponent(): LoginActivityComponent.Factory } Component Treeの接ぎ木
SingletonCImpl Component Treeの接ぎ木 ApplicationC MainActivityC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC
FragmentC ApplicationM RepositoryM
SingletonCImpl Component Treeの接ぎ木 ApplicationC MainActivityC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC
FragmentC ApplicationM RepositoryM
@Singleton @Component(modules = [ AndroidInjectionModule::class, ApplicationModule::class, RepositoryModule::class, ActivityModule::class,]) @InstallIn(SingletonComponent::class) @EntryPoint
interface ApplicationComponent : AndroidInjector<App> { fun loginActivityComponent(): LoginActivityComponent.Factory } Component Treeの接ぎ木
@InstallIn(SingletonComponent::class) @Module(includes = [ AndroidInjectionModule::class, ApplicationModule::class, RepositoryModule::class, ActivityModule::class,]) interface AggregatorModule
@InstallIn(SingletonComponent::class) @EntryPoint interface ApplicationComponent : AndroidInjector<App> { fun loginActivityComponent(): LoginActivityComponent.Factory } Component Treeの接ぎ木
SingletonCImpl Component Treeの接ぎ木 ApplicationC MainActivityC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC
FragmentC ApplicationM RepositoryM
class App : Application(), HasAndroidInjector { val component: ApplicationComponent =
DaggerApplicationComponent.factory().create(this) @Inject lateinit var androidInjector: DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> = androidInjector } Applicationの変更
class App : Application(), HasAndroidInjector { val component: ApplicationComponent =
DaggerApplicationComponent.factory().create(this) @Inject lateinit var androidInjector: DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> = androidInjector } Applicationの変更
class App : Application(), HasAndroidInjector { val component: ApplicationComponent by
lazy { EntryPoints.get(this, ApplicationComponent::class.java) } @Inject lateinit var androidInjector: DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> = androidInjector } Applicationの変更
class App : Application(), HasAndroidInjector { val component: ApplicationComponent by
lazy { EntryPoints.get(this, ApplicationComponent::class.java) } @Inject lateinit var androidInjector: DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> = androidInjector } Applicationの変更
@HiltAndroidApp class App : Application(), HasAndroidInjector { val component: ApplicationComponent
by lazy { EntryPoints.get(this, ApplicationComponent::class.java) } @Inject lateinit var androidInjector: DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> = androidInjector } Applicationの変更
@HiltAndroidApp class App : Application(), HasAndroidInjector { val component: ApplicationComponent
by lazy { EntryPoints.get(this, ApplicationComponent::class.java) } @Inject lateinit var androidInjector: DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> = androidInjector } Applicationの変更
@HiltAndroidApp class App : Application(), HasAndroidInjector { val component: ApplicationComponent
by lazy { EntryPoints.get(this, ApplicationComponent::class.java) } @Inject lateinit var androidInjector: DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> = androidInjector } Applicationの変更
SingletonCImpl 現時点のComponent Tree ApplicationC MainActivityC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC
FragmentC ApplicationM RepositoryM
現時点のComponent Tree ApplicationC MainActivityC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC FragmentC
ApplicationM RepositoryM
移行ステップ 1. gradleの設定 2. Component Treeの接ぎ木 3. Scopeアノテーションの置き換え 4. MainActivityの移行
5. Loginの移行 6. MainFragmentの移行 7. Activityのお掃除 8. dagger.androidの除去 9. ApplicationComponentの削除
@Module interface MainActivityModule { @ActivityScope @ContributesAndroidInjector(modules = [MainFragmentModule::class]) fun contributesMainActivity():
MainActivity } @Module interface MainFragmentModule { @FragmentScope @ContributesAndroidInjector fun contributesMainFragment(): MainFragment } Scopeアノテーションの置き換え
@Module interface MainActivityModule { @ActivityScope @ContributesAndroidInjector(modules = [MainFragmentModule::class]) fun contributesMainActivity():
MainActivity } @Module interface MainFragmentModule { @FragmentScope @ContributesAndroidInjector fun contributesMainFragment(): MainFragment } Scopeアノテーションの置き換え
import dagger.hilt.android.scopes.ActivityScoped import dagger.hilt.android.scopes.FragmentScoped @Module interface MainActivityModule { @ActivityScoped @ContributesAndroidInjector(modules
= [MainFragmentModule::class]) fun contributesMainActivity(): MainActivity } @Module interface MainFragmentModule { @FragmentScoped @ContributesAndroidInjector fun contributesMainFragment(): MainFragment } Scopeアノテーションの置き換え
import dagger.hilt.android.scopes.ActivityScoped import dagger.hilt.android.scopes.FragmentScoped @Module interface MainActivityModule { @ActivityScoped @ContributesAndroidInjector(modules
= [MainFragmentModule::class]) fun contributesMainActivity(): MainActivity } @Module interface MainFragmentModule { @FragmentScoped @ContributesAndroidInjector fun contributesMainFragment(): MainFragment } Scopeアノテーションの置き換え
移行ステップ 1. gradleの設定 2. Component Treeの接ぎ木 3. Scopeアノテーションの置き換え 4. MainActivityの移行
5. Loginの移行 6. MainFragmentの移行 7. Activityのお掃除 8. dagger.androidの除去 9. ApplicationComponentの削除
移行 = 担当Component変更 ApplicationC MainActivityC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC
FragmentC
移行 = 担当Component変更 ApplicationC MainActivityC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC
FragmentC MainActivity担当
移行 = 担当Component変更 ApplicationC MainActivityC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC
FragmentC MainActivity担当
class MainActivity : AppCompatActivity(), HasAndroidInjector { @Inject lateinit var androidInjector:
DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> = androidInjector override fun onCreate(savedInstanceState: Bundle?) { AndroidInjection.inject(this) super.onCreate(savedInstanceState) } } MainActivityの移行
class MainActivity : AppCompatActivity(), HasAndroidInjector { @Inject lateinit var androidInjector:
DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> = androidInjector override fun onCreate(savedInstanceState: Bundle?) { AndroidInjection.inject(this) super.onCreate(savedInstanceState) } } MainActivityの移行
class MainActivity : AppCompatActivity(), HasAndroidInjector { @Inject lateinit var androidInjector:
DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> = androidInjector override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } } MainActivityの移行
@AndroidEntryPoint class MainActivity : AppCompatActivity(), HasAndroidInjector { @Inject lateinit var
androidInjector: DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> = androidInjector override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } } MainActivityの移行
@AndroidEntryPoint class MainActivity : AppCompatActivity(), HasAndroidInjector { @Inject lateinit var
androidInjector: DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> = androidInjector override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) } } MainActivityの移行
MainActivityのComponent削除 ApplicationC MainActivityC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC FragmentC
MainActivityのComponent削除 ApplicationC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC FragmentC
MainFragmentのComponent接ぎ木 ApplicationC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC FragmentC
MainFragmentのComponent接ぎ木 ApplicationC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC FragmentC
MainFragmentのComponent接ぎ木 ApplicationC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC FragmentC MainFragmentModule
@InstallIn(ActivityComponent::class) @Module( includes = [ MainFragmentModule::class, ] ) interface DaggerAndroidActivityModule
MainFragmentのComponent接ぎ木
移行ステップ 1. gradleの設定 2. Component Treeの接ぎ木 3. Scopeアノテーションの置き換え 4. MainActivityの移行
5. Loginの移行 6. MainFragmentの移行 7. Activityのお掃除 8. dagger.androidの除去 9. ApplicationComponentの削除
Loginの移行 ApplicationC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC FragmentC
Loginの移行 ApplicationC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC FragmentC
Loginの移行 ApplicationC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC FragmentC LoginActivity担当 LoginFragment担当
Loginの移行 ApplicationC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC FragmentC LoginActivity担当 LoginFragment担当
class LoginActivity : AppCompatActivity() { lateinit var component: LoginActivityComponent override
fun onCreate(savedInstanceState: Bundle?) { component = (applicationContext as App).component .loginActivityComponent().create(this) component.inject(this) super.onCreate(savedInstanceState) } } LoginActivityの移行
class LoginActivity : AppCompatActivity() { lateinit var component: LoginActivityComponent override
fun onCreate(savedInstanceState: Bundle?) { component = (applicationContext as App).component .loginActivityComponent().create(this) component.inject(this) super.onCreate(savedInstanceState) } } LoginActivityの移行
class LoginActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) } } LoginActivityの移行
@AndroidEntryPoint class LoginActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?)
{ super.onCreate(savedInstanceState) } } LoginActivityの移行
@AndroidEntryPoint class LoginActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?)
{ super.onCreate(savedInstanceState) /* 省略 */ } } LoginActivityの移行
class LoginFragment: Fragment() { lateinit var component: LoginFragmentComponent override fun
onAttach(context: Context) { super.onAttach(context) component = (requireActivity() as LoginActivity).component .loginFragmentComponent().create(this) component.inject(this) } } LoginFragmentの移行
class LoginFragment: Fragment() { lateinit var component: LoginFragmentComponent override fun
onAttach(context: Context) { super.onAttach(context) component = (requireActivity() as LoginActivity).component .loginFragmentComponent().create(this) component.inject(this) } } LoginFragmentの移行
class LoginFragment: Fragment() { override fun onAttach(context: Context) { super.onAttach(context)
} } LoginFragmentの移行
@AndroidEntryPoint class LoginFragment: Fragment() { override fun onAttach(context: Context) {
super.onAttach(context) } } LoginFragmentの移行
@AndroidEntryPoint class LoginFragment: Fragment() { override fun onAttach(context: Context) {
super.onAttach(context) /* 省略 */ } } LoginFragmentの移行
LoginのComponent削除 ApplicationC MainFragmentC LoginActivityC LoginFragmentC SingletonC ActivityC FragmentC
LoginのComponent削除 ApplicationC MainFragmentC SingletonC ActivityC FragmentC
LoginのComponent削除 ApplicationC MainFragmentC SingletonC ActivityC FragmentC
移行ステップ 1. gradleの設定 2. Component Treeの接ぎ木 3. Scopeアノテーションの置き換え 4. MainActivityの移行
5. Loginの移行 6. MainFragmentの移行 7. Activityのお掃除 8. dagger.androidの除去 9. ApplicationComponentの削除
MainFragmentの移行 ApplicationC MainFragmentC SingletonC ActivityC FragmentC MainFragment担当
MainFragmentの移行 ApplicationC MainFragmentC SingletonC ActivityC FragmentC MainFragment担当
class MainFragment : Fragment() { override fun onAttach(context: Context) {
AndroidSupportInjection.inject(this) super.onAttach(context) /* 省略 */ } } MainFragmentの移行
class MainFragment : Fragment() { override fun onAttach(context: Context) {
AndroidSupportInjection.inject(this) super.onAttach(context) /* 省略 */ } } MainFragmentの移行
class MainFragment : Fragment() { override fun onAttach(context: Context) {
super.onAttach(context) /* 省略 */ } } MainFragmentの移行
@AndroidEntryPoint class MainFragment : Fragment() { override fun onAttach(context: Context)
{ super.onAttach(context) /* 省略 */ } } MainFragmentの移行
@AndroidEntryPoint class MainFragment : Fragment() { override fun onAttach(context: Context)
{ super.onAttach(context) /* 省略 */ } } MainFragmentの移行
MainFragmentのComponent削除 ApplicationC MainFragmentC SingletonC ActivityC FragmentC
MainFragmentのComponent削除 ApplicationC SingletonC ActivityC FragmentC
移行ステップ 1. gradleの設定 2. Component Treeの接ぎ木 3. Scopeアノテーションの置き換え 4. MainActivityの移行
5. Loginの移行 6. MainFragmentの移行 7. Activityのお掃除 8. dagger.androidの除去 9. ApplicationComponentの削除
@AndroidEntryPoint class MainActivity : AppCompatActivity(), HasAndroidInjector { @Inject lateinit var
androidInjector: DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> = androidInjector /* 省略 */ } MainActivityからdagger.androidを除去
@AndroidEntryPoint class MainActivity : AppCompatActivity(), HasAndroidInjector { @Inject lateinit var
androidInjector: DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> = androidInjector /* 省略 */ } MainActivityからdagger.androidを除去
@AndroidEntryPoint class MainActivity : AppCompatActivity() { /* 省略 */ }
MainActivityからdagger.androidを除去
移行ステップ 1. gradleの設定 2. Component Treeの接ぎ木 3. Scopeアノテーションの置き換え 4. MainActivityの移行
5. Loginの移行 6. MainFragmentの移行 7. Activityのお掃除 8. dagger.androidの除去 9. ApplicationComponentの削除
Activity / Fragmentの移行が完了したら ApplicationC SingletonC ActivityC FragmentC
@HiltAndroidApp class App : Application(), HasAndroidInjector { @Inject lateinit var
androidInjector: DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> = androidInjector val component: ApplicationComponent by lazy { EntryPoints.get(this, ApplicationComponent::class.java) } } Appからdagger.androidを除去
@HiltAndroidApp class App : Application(), HasAndroidInjector { @Inject lateinit var
androidInjector: DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> = androidInjector val component: ApplicationComponent by lazy { EntryPoints.get(this, ApplicationComponent::class.java) } } Appからdagger.androidを除去
@HiltAndroidApp class App : Application() { val component: ApplicationComponent by
lazy { EntryPoints.get(this, ApplicationComponent::class.java) } } Appからdagger.androidを除去
@InstallIn(SingletonComponent::class) @EntryPoint interface ApplicationComponent : AndroidInjector<App> @InstallIn(SingletonComponent::class) @Module( includes =
[ AndroidInjectionModule::class, ApplicationModule::class, RepositoryModule::class, ] ) interface AggregatorModule Component Treeからdagger.androidを除去
@InstallIn(SingletonComponent::class) @EntryPoint interface ApplicationComponent : AndroidInjector<App> @InstallIn(SingletonComponent::class) @Module( includes =
[ AndroidInjectionModule::class, ApplicationModule::class, RepositoryModule::class, ] ) interface AggregatorModule Component Treeからdagger.androidを除去
@InstallIn(SingletonComponent::class) @EntryPoint interface ApplicationComponent @InstallIn(SingletonComponent::class) @Module( includes = [ ApplicationModule::class,
RepositoryModule::class, ] ) interface AggregatorModule Component Treeからdagger.androidを除去
移行ステップ 1. gradleの設定 2. Component Treeの接ぎ木 3. Scopeアノテーションの置き換え 4. MainActivityの移行
5. Loginの移行 6. MainFragmentの移行 7. Activityのお掃除 8. dagger.androidの除去 9. ApplicationComponentの削除
ApplicationComponent削除 ApplicationC SingletonC ActivityC FragmentC
ApplicationComponent削除 ApplicationC SingletonC ActivityC FragmentC
@HiltAndroidApp class App : Application() { val component: ApplicationComponent by
lazy { EntryPoints.get(this, ApplicationComponent::class.java) } } @InstallIn(SingletonComponent::class) @EntryPoint interface ApplicationComponent ApplicationComponentを削除
@HiltAndroidApp class App : Application() { val component: ApplicationComponent by
lazy { EntryPoints.get(this, ApplicationComponent::class.java) } } @InstallIn(SingletonComponent::class) @EntryPoint interface ApplicationComponent ApplicationComponentを削除
@HiltAndroidApp class App : Application() { /* 省略 */ }
ApplicationComponentを削除
Well done 👏
Daggerの捉え方 dagger.androidとHiltの思想 Hiltに段階的に移行する FAQ Agenda 01 02 03 04
Q. LoginActivityのような具象クラスを扱いたい @ActivityScope @Subcomponent interface LoginActivityComponent { @Subcomponent.Factory interface Factory
{ fun create(@BindsInstance activity: LoginActivity): LoginActivityComponent } }
Q. LoginActivityのような具象クラスを扱いたい A. castしましょう @InstallIn(ActivityComponent::class) @Module class LoginActivityModule { @Provides
fun providesLoginActivity(activity: Activity): LoginActivity? = activity as? LoginActivity? }
Q. LoginActivityのような具象クラスを扱いたい A. castしましょう @InstallIn(ActivityComponent::class) @Module class LoginFeatureModule { @Provides
fun providesLoginActivity(activity: Activity): LoginFeature? = (activity as? HasLoginFeature?)?.loginFeature }
Q. ActivityごとにModuleを切り替えたい @Module interface SignupActivityModule { @ActivityScope @ContributesAndroidInjector(modules = [EmailModule::class])
fun contributesEmailSignupActivity(): EmailSignupActivity @ActivityScope @ContributesAndroidInjector(modules = [SnsModule::class]) fun contributesSnsSignupActivity(): SnsSignupActivity }
Q. ActivityごとにModuleを切り替えたい @Module interface SignupActivityModule { @ActivityScope @ContributesAndroidInjector(modules = [EmailModule::class])
fun contributesEmailSignupActivity(): EmailSignupActivity @ActivityScope @ContributesAndroidInjector(modules = [SnsModule::class]) fun contributesSnsSignupActivity(): SnsSignupActivity }
Q. ActivityごとにModuleを切り替えたい @Module interface EmailModule { @Binds fun bindsFeature(feature: EmailSignupFeature):
SignupFeature } @Module interface SnsModule { @Binds fun bindsFeature(feature: SnsSignupFeature): SignupFeature }
Q. ActivityごとにModuleを切り替えたい A. 切り替えたいのはModuleではなくインスタンス @InstallIn(ActivityComponent::class) @Module class SignupModule { @Provides
fun providesFeature(activity: Activity): SignupFeature = when (activity) { is EmailSignupActivity -> EmailSignupFeature() is SnsSignupActivity -> SnsSignupFeature() else -> error("Invalid Activity") } }
Q. FCMServiceにinjectするとUIテストが落ちるのですが @AndroidEntryPoint class MyFirebaseMessagingService : FirebaseMessagingService() { @Inject lateinit
var useCase: RegisterToken override fun onNewToken(token: String) { super.onNewToken(token) useCase.execute(token) } }
Q. FCMServiceにinjectするとUIテストが落ちるのですが C Tree @Before @After C Tree @Before @After
C Tree @Before @After
Q. FCMServiceにinjectするとUIテストが落ちるのですが ContentProvider 初期化 C Tree @Before @After C Tree
@Before @After C Tree @Before @After
Q. FCMServiceにinjectするとUIテストが落ちるのですが A. テストでuseCase実行を諦めましょう class MyFirebaseMessagingService : FirebaseMessagingService() { @EntryPoint
@InstallIn(SingletonComponent::class) interface LocalEntryPoint { fun useCase(): RegisterToken } }
Q. FCMServiceにinjectするとUIテストが落ちるのですが A. テストでuseCase実行を諦めましょう class MyFirebaseMessagingService : FirebaseMessagingService() { private
val useCase: RegisterToken? by lazy { try { EntryPointAccessors.fromApplication( applicationContext, LocalEntryPoint::class.java ).useCase() } catch (t: Throwable) { null } } }
Q. FCMServiceにinjectするとUIテストが落ちるのですが A. テストでuseCase実行を諦めましょう class MyFirebaseMessagingService : FirebaseMessagingService() { override
fun onNewToken(token: String) { super.onNewToken(token) useCase?.execute(token) } }
Q. 今度はApplicationでUIテストが落ちるのですが ContentProvider 初期化 C Tree @Before @After C Tree
@Before @After C Tree @Before @After
Q. 今度はApplicationでUIテストが落ちるのですが App#onCreate ContentProvider 初期化 C Tree @Before @After C
Tree @Before @After C Tree @Before @After
Q. 今度はApplicationでUIテストが落ちるのですが A. 初期化処理を遅延実行しましょう
open class BaseApp : Application() { @EntryPoint @InstallIn(SingletonComponent::class) interface AppEntryPoint
{ fun appDependencies(): AppDependencies } private val entryPoint: AppEntryPoint by lazy { EntryPointAccessors.fromApplication(this, AppEntryPoint::class.java) } }
open class BaseApp : Application() { override fun onCreate() {
super.onCreate() initializeOnCreate() } protected open fun initializeOnCreate() { // ここに必要な初期化処理 } }
@CustomTestApplication(AndroidTestApp::class) interface HiltAndroidTestApp class AndroidTestApp : BaseApp() { private val
isInitialized = AtomicBoolean(false) override fun initializeOnCreate() { // 初期化はここでは実施しない } fun initialize() { if (isInitialized.compareAndSet(false, true)) { super.initializeOnCreate() } } }
@CustomTestApplication(AndroidTestApp::class) interface HiltAndroidTestApp class AndroidTestApp : BaseApp() { private val
isInitialized = AtomicBoolean(false) override fun initializeOnCreate() { // 初期化はここでは実施しない } fun initialize() { if (isInitialized.compareAndSet(false, true)) { super.initializeOnCreate() } } }
@CustomTestApplication(AndroidTestApp::class) interface HiltAndroidTestApp class AndroidTestApp : BaseApp() { private val
isInitialized = AtomicBoolean(false) override fun initializeOnCreate() { // 初期化はここでは実施しない } fun initialize() { if (isInitialized.compareAndSet(false, true)) { super.initializeOnCreate() } } }
@CustomTestApplication(AndroidTestApp::class) interface HiltAndroidTestApp class AndroidTestApp : BaseApp() { private val
isInitialized = AtomicBoolean(false) override fun initializeOnCreate() { // 初期化はここでは実施しない } fun initialize() { if (isInitialized.compareAndSet(false, true)) { super.initializeOnCreate() } } }
class AppInitializeAndroidTestRule : TestRule { override fun apply(base: Statement, description:
Description): Statement = object : Statement() { override fun evaluate() { val app = ApplicationProvider.getApplicationContext<AndroidTestApp>() app.initialize() base.evaluate() } } }
class AppInitializeAndroidTestRule : TestRule { override fun apply(base: Statement, description:
Description): Statement = object : Statement() { override fun evaluate() { val app = ApplicationProvider.getApplicationContext<AndroidTestApp>() app.initialize() base.evaluate() } } }
fun androidTestRule(testInstance: Any): TestRule = RuleChain.outerRule(HiltAndroidRule(testInstance)) .around(AppInitializeAndroidTestRule()) @HiltAndroidTest @RunWith(AndroidJUnit4::class) class
SomeUiTest { @get:Rule val rule = androidTestRule(this) }
Q. 〇〇を□□したい!
Q. 〇〇を□□したい! A. Component Treeを思い浮かべて、原理から考えよう
Thanks!