Presentation is loading. Please wait.

Presentation is loading. Please wait.

Dagger + Android contributions

Similar presentations


Presentation on theme: "Dagger + Android contributions"— Presentation transcript:

1 Dagger + Android contributions
Sigitas Atkočaitis

2 Why Dagger

3 private fun setUpInjection() {
navigator = Navigator() locationPermissionManager = LocationPermissionManager(this) val latLngMapper = LatLngMapperImpl() val converter = BitmapDescriptorConverter(resources) val markerGenerator = MarkerGeneratorImpl(latLngMapper, converter) val adapter = MarkerInfoWindowAdapter(activity!! .layoutInflater) val clusterIconProvider = ClusterIconProviderImpl(converter) map = PharmaciesMapHolder(markerGenerator, latLngMapper, adapter, this, clusterIconProvider) val retriever = DependencyRetriever.from(context!!) val service = retriever.getService(LoadPharmaciesService::class.java) val dbHelper = DependencyRetriever.from(context!!).databaseHelper val pharmaciesRepository = PharmaciesRepositoryImpl(dbHelper) val prefRepository = PreferenceRepository(context!!) val expireModel = PharmaciesExpireModelImpl(prefRepository) val pharmaciesModel = LoadPharmaciesModelImpl(service, pharmaciesRepository, expireModel, Schedulers.io()) locationService = DefaultLocationService(context!!) val model = PharmaciesMapModelImpl(locationService!!, pharmaciesModel) val resolver = NetworkErrorResolver( DefaultNetworkErrorMessenger(resources)) presenter = PharmaciesMapPresenterImpl(model, AndroidSchedulers.mainThread(), resolver) }

4 val storageFactory = PreferenceStorageFactory();
val antennaStorage = storageFactory.create(context, Antenna::class); val amplifierStorage = storageFactory.create(context, Amplifier::class); val attenuationStorage = storageFactory.create(context, Attenuation::class); val antennaRepository = AntennaRepository(antennaStorage); val amplifierRepository = AmplifierRepository(amplifierStorage); val attenuationRepository = AttenuationRepository(attenuationStorage); val bandRegionStorage = storageFactory.create(context, BandRegion::class); val colorRangeStorage = storageFactory.create(context, ColorRange::class); val colorRepository = ColorRepositoryImpl(colorRangeStorage); val audioToneStorage = storageFactory.create(context, AudioTone::class); val audioToneRepository = AudioToneRepository(audioToneStorage); val logDirRepository = SessionDirRepositoryImpl( storageFactory.create(context, String::class)); val tileDirRepository = TilesDirRepository( val formStorage = storageFactory.create(context, SettingsForm::class); val bandRegionHolderStorage = storageFactory.create(context, BandRegionHolder::class); val bandListProvider = DefaultBandListProvider(); val bandRegionRepository = BandRegionListRepositoryImpl(bandRegionStorage, bandRegionHolderStorage, bandListProvider); val settingsFormRepository = SettingsFormRepositoryImpl( antennaRepository, amplifierRepository, attenuationRepository, audioToneRepository, bandRegionRepository, colorRepository, logDirRepository, tileDirRepository, formStorage ); val computationScheduler = Schedulers.computation(); val mainScheduler = AndroidSchedulers.mainThread(); val model = SettingsFragmentModel( bandRegionRepository, antennaRepository, amplifierRepository, attenuationRepository, colorRepository, audioToneRepository, formProvider, settingsFormRepository, computationScheduler ); val calculator = AutoColorRangeCalculatorImpl(); val selectedListStorage = storageFactory.create(context, BandRegionHolder::class); val bandRegionListRepository = BandRegionListRepositoryImpl(bandRegionStorage, selectedListStorage, bandListProvider); return SettingsFragmentPresenter( model, calculator, mainScheduler, updateChecker, SeewaveApplication.getRecorderManager(context).getFileManager(), bandRegionListRepository

5 Why Dagger More Testable Code More Readable Code More Reusable Code
Reduced Dependency Carrying

6 Main Dagger parts @Component @SubComponent @Module
@ContributesAndroidInjector

7 @DaggerScope(BaseApplication::class)
@Component( modules = [ AndroidSupportInjectionModule::class, AppModule::class, ActivitiesModule::class, SchedulerModule::class, NetworkModule::class, ImageModule::class ] ) interface AppComponent : AndroidInjector<BaseApplication> { @Component.Builder abstract class Builder : AndroidInjector.Builder<BaseApplication>() } open class BaseApplication : DaggerApplication() { override fun applicationInjector(): AndroidInjector<out DaggerApplication> = DaggerAppComponent.builder().create(this)

8 @Module abstract class AppModule private constructor() { @Binds abstract fun bindContext(app: BaseApplication): Context companion object { @JvmStatic @Provides fun provideResources(context: Context): Resources = context.resources }

9 @Module abstract class SchedulerModule private constructor() { companion object { @MainThread fun provideMainScheduler(): Scheduler = AndroidSchedulers.mainThread() @ComputationThread fun provideComputationScheduler(): Scheduler = Schedulers.computation() @IoThread fun provideIoScheduler(): Scheduler = Schedulers.io() @NetworkThread } @Qualifier annotation class NetworkThread

10 @Module abstract class ImageModule private constructor() { @Binds @Reusable abstract fun bindImageLoader(loader: GlideImageLoader): ImageLoader }

11 @Module abstract class NetworkModule private constructor() { companion object { ... @JvmStatic @Provides @DaggerScope(BaseApplication::class) fun provideRetrofitClient( @NetworkThread networkScheduler: Scheduler ): Retrofit = Retrofit.Builder() .addCallAdapterFactory( RxJava2CallAdapterFactory.createWithScheduler(networkScheduler) ) .baseUrl(BuildConfig.API_ENDPOINT) .build() } @Scope annotation class DaggerScope(val value: KClass<*>)

12 abstract class ActivitiesModule private constructor() {
@DaggerScope(MainActivity::class) @ContributesAndroidInjector(modules = [MainActivityModule::class]) abstract fun bindMainActivity(): MainActivity } class MainActivity : DaggerAppCompatActivity(){ ... }

13 @dagger.Module abstract class MainActivityModule private constructor() { @DaggerScope(MainFragment::class) @ContributesAndroidInjector(modules = [MainScreen.Module::class]) abstract fun bindArticleListFragment(): MainFragment }

14 object MainScreen { interface MainPresenter : BasePresenter<MainScreen.View> {...} interface View {...} @dagger.Module abstract class Module private constructor() { @Binds @DaggerScope(MainFragment::class) abstract fun bindPresenter(presenter: MainPresenterImpl): MainPresenter companion object { @Provides @JvmStatic fun provideService(retrofit: Retrofit) = retrofit.create<NewsService>() }

15 class MainPresenterImpl @Inject constructor(
private val service: NewsService, @MainThread private val scheduler: Scheduler ) : BasePresenterImpl<MainScreen.View>(), MainScreen.MainPresenter { ... } class MainFragment : DaggerFragment(), MainScreen.View { @Inject lateinit var presenter: MainScreen.MainPresenter override fun onCreateView(...): View? { presenter.takeView(this) return fragmentView

16 Unit tests You don’t have to use Dagger for single-class unit tests
If you want to write a small unit test that tests only class, you don’t need to use Dagger to instantiate that class. If you want to write a traditional unit test, you can directly call constructor and methods and set fields, if any, passing fake or mock dependencies directly, just as you would if they weren’t annotated.

17 class MainPresenterImplTest {
private val service = mock<NewsService>() private val view = mock<MainScreen.View>() private val scheduler = TestScheduler() private val presenter = MainPresenterImpl(service, scheduler) private val articles = listOf<Article>(mock()) private val articlesResponse = ArticlesResponse("200", 0, articles) @Test fun exampleUnitTest() { given(service.getArticleList()).willReturn(Single.just(articlesResponse)) presenter.takeView(view) presenter.onViewCreated() scheduler.triggerActions() verify(view).showArticles(articles) }

18 Instrumentation testing

19 @DaggerScope(BaseApplication::class)
@Component( modules = [ AndroidSupportInjectionModule::class, InstrumentationModule::class, AppModule::class, ActivitiesModule::class, InstrumentationSchedulerModule::class, NetworkModule::class, ImageModule::class ] ) interface TestAppComponent : AndroidInjector<TestBaseApplication> { @Component.Builder abstract class Builder : AndroidInjector.Builder<TestBaseApplication>() }

20 open class TestApplicationRunner : AndroidJUnitRunner() {
@Throws(Exception::class) override fun newApplication(loader: ClassLoader, className: String, context: Context): Application { return super.newApplication(loader, TestBaseApplication::class.java.name, context) } class TestBaseApplication : BaseApplication() { override fun applicationInjector(): AndroidInjector<out DaggerApplication> { return DaggerTestAppComponent.builder().create(this)

21 @Module abstract class InstrumentationSchedulerModule private constructor() { companion object { ... @Provides fun provideNetworkScheduler(executor: IdlingThreadPoolExecutor): Scheduler = Schedulers.from(executor) @DaggerScope(BaseApplication::class) fun provideIdlingThreadPoolExecutor(): IdlingThreadPoolExecutor = IdlingThreadPoolExecutor(...) }

22 class ExampleInstrumentedTest {
@get:Rule var rule = ActivityTestRule(MainActivity::class.java) @Test fun exampleTest() { onView(withId(R.id.listRecyclerView)).check(matches(hasMinimumChildCount(1))) onView(withId(R.id.listRecyclerView)).perform(ViewActions.swipeUp()) }

23 Alternative: Koin.io

24 @DaggerScope(BaseApplication::class)
@Component( modules = [ AndroidSupportInjectionModule::class, AppModule::class, ActivitiesModule::class, SchedulerModule::class, NetworkModule::class, ImageModule::class ] ) interface AppComponent : AndroidInjector<BaseApplication> { @Component.Builder abstract class Builder : AndroidInjector.Builder<BaseApplication>() }

25 open class BaseApplication : Application() {
override fun onCreate() { startKoin(KoinApplication.create().apply { androidLogger(Level.DEBUG) modules( PluginsModule(), AppModule(), SchedulerModule(), NetworkModule(), ImageModule(), MainScreen.Module() ) }) }

26 @Module abstract class AppModule private constructor() { @Binds abstract fun bindContext(app: BaseApplication): Context companion object { @JvmStatic @Provides fun provideResources(context: Context): Resources = context.resources }

27 fun AppModule() = module {
single { androidContext().resources } }

28 @dagger.Module abstract class Module private constructor() { @Binds @DaggerScope(MainFragment::class) abstract fun bindPresenter(presenter: MainPresenterImpl): MainPresenter companion object { @Provides @JvmStatic fun provideService(retrofit: Retrofit) = retrofit.create<NewsService>() }

29 fun AppModule()= module {
single { androidContext().resources } } fun Module() = module { factory<Presenter> { MainPresenter(service = get(), scheduler = get("main")) } factory<NewsService> { get<Retrofit>().create() }

30 abstract class NetworkModule private constructor() {
companion object { @JvmStatic @Provides @DaggerScope(BaseApplication::class) fun provideRetrofitClient( @NetworkThread networkScheduler: Scheduler ): Retrofit = Retrofit.Builder() .addCallAdapterFactory( RxJava2CallAdapterFactory.createWithScheduler(networkScheduler) ) .baseUrl(BuildConfig.API_ENDPOINT) .build() }

31 fun AppModule() = module {
single { androidContext().resources } } fun Module() = module { factory<Presenter> { MainPresenter(service = get(), scheduler = get("main")) } factory<NewsService> { get<Retrofit>().create() } fun NetworkModule() = module { single { Retrofit = Retrofit.Builder() .addCallAdapterFactory( RxJava2CallAdapterFactory.createWithScheduler(get("network")) ) .baseUrl(BuildConfig.API_ENDPOINT) .build()

32 Singleton scopes based only on lifecycle, no hierarchy Unstable, prone to change API

33 Questions?


Download ppt "Dagger + Android contributions"

Similar presentations


Ads by Google