Muselee is a demo app which allows the user to browse popular music artists. It is not intended to be a fully-featured user app, but a vehicle to explore good app architecture, how to implement current best-practice, and explore how the two often go hand in hand. Moreover it will be used to explore how implementing some specific patterns can help to keep our app both maintainable, and easy to extend.
Let’s start building our code and look at what goes in to our Application module. This should be pretty lean, and should essentially be wiring up the feature modules. One of the key areas that we’ve already looked at is Dependency Injection using Dagger 2, but we’ll need to extend a little the basic behaviour that we implemented earlier. The first thing we need to do is update the ApplicationModule
. This needs to provide an Application context to any components which may require one – and there will be a couple. The Application itself is defined within the app
module so this is the only place where we can provide this because no other modules should have a dependency upon app
:
@Module object ApplicationModule { @Provides @JvmStatic @Singleton internal fun provideApplication(app: MuseleeApplication): Application = app @Provides @JvmStatic @Singleton fun providesConnectivityManager(app: Application): ConnectivityManager = app.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager }
The first thing worth noting is that we have changed this from an abstract class to an object. This makes it much cleaner to define singleton instances because there will only ever be one copy of ApplicationModule, and the fact that I have annotated each of the methods with @JvmStatic
means that these will be static methods also.
There are two provider methods here, one take a MuseleeApplication
instance and returns a generic Application
context. This is to ensure that none of the other modules need to know about MuseleeApplication
– they only need to know at worst about Application
but most commonly Context
.
The other thing worth noting is that we have removed the Activity binding method that we created previously. This gets moved to a separate module because we cannot use @ContributesAndroidInjector
within an object – we need an abstract class so that Dagger can create the appropriate method:
@Module abstract class ActivityModule { @ContributesAndroidInjector abstract fun bindMuseleeActivity(): MuseleeActivity }
This now needs to be wired in to ApplicationComponent
:
@Component( modules = [ AndroidInjectionModule::class, ApplicationModule::class, ActivityModule::class, TopArtistsModule::class ] ) interface ApplicationComponent : AndroidInjector{ @Component.Builder abstract class Builder : AndroidInjector.Builder () }
That’s it for the DI side of things. ApplicationComponent
is the main entry point in terms of DI, and later on we’ll alter TopArtistsModule
to perform all of the necessary initialisation for that specific module. All we need to know for now is that it will require a Context, and that can now be provided by Dagger thanks to the changes we made to ApplicationModule
.
The only other thing that we need to do in the app
module is perform some setup for Glide. We’ll be using Glide for image loading, and for Glide to work properly we need to provide an AppGlideModule
.
@GlideModule class MuseleeAppGlideModule : AppGlideModule() { override fun applyOptions(context: Context, builder: GlideBuilder) { super.applyOptions(context, builder) if (BuildConfig.DEBUG) { builder.setLogLevel(Log.VERBOSE) } } }
The Glide annotation processor will build some initialisation code around this thanks to the @GlideModule
annotation. We use this opportunity to turn on verbose logging for debug builds. If we needed to register any application level Glide components, we could do it here, but we could also register module-specific components by implementing LibraryGlideModule
instances within the modules themselves.
There’s nothing particularly groundbreaking going on here but, the DI framework that we now have is actually really flexible, and will make it easy to perform module-specific configuration within the modules themselves.
In the next article we’ll take a look at the core
module and look at some of the common functionality that belong there.
The source code for this article is available here.
© 2019, Mark Allison. All rights reserved.
Copyright © 2019 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.