Skip to content

Instantly share code, notes, and snippets.

@murki
Last active June 10, 2021 03:59
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save murki/7c99e2d011c663deb25267e5462cc02d to your computer and use it in GitHub Desktop.
Save murki/7c99e2d011c663deb25267e5462cc02d to your computer and use it in GitHub Desktop.
The poor man's Dagger: Exemplifying how to implement dependency injection on Android by (ab)using ApplicationContext's getSystemService(). Here we attain inversion of control without the need of any external library. We also use Kotlin's extension methods to provide a friendlier, strongly-typed API to locate dependencies.
class PlacesActivity : AppCompatActivity() {
private lateinit var placesPresenter: PlacesPresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// This is how you instantiate your Presenter while the service locator resolves all of its dependencies
// Note that the explicit type argument <PlacesPresenter> is not even necessary since Kotlin can infer the type
placesPresenter = application.getSystemService<PlacesPresenter>()
}
}
class ServiceLocatorApplication : Application() {
/**
* Dependency injection using service locator pattern
*/
override fun getSystemService(name: String?): Any {
// All this logic can be extracted into a separate "Module" class
return when (name) {
// We use the full class' name as the key
PlacesPresenter::class.java.name -> PlacesPresenter(getSystemService<ILocationRepository>(), getSystemService<IPlacesRepository>())
ILocationRepository::class.java.name -> GmsLocationRepository(getSystemService<FusedLocationProviderClient>())
IPlacesRepository::class.java.name -> GmsPlacesRepository(getSystemService<GeoDataClient>())
FusedLocationProviderClient::class.java.name -> LocationServices.getFusedLocationProviderClient(this)
GeoDataClient::class.java.name -> Places.getGeoDataClient(this, null)
else -> super.getSystemService(name)
}
}
}
/**
* Generic strongly-typed extension method for injection
*/
inline fun <reified T : Any> Application.getSystemService() : T {
// We extract the class name from the generic type
return this.getSystemService(T::class.java.name) as T
}
@Lamartio
Copy link

Lamartio commented May 18, 2018

As an addition you can override the Activity's base context to have an acitivity scope.

// override this method in your activity
override fun attachBaseContext(newBase: Context) =
        super.attachBaseContext(SystemServiceContext(newBase))
class SystemServiceContext(private val context: Context) : ContextWrapper(context) {

    override fun getSystemService(name: String?): Any? =
             super.getSystemService(name) ?: context.applicationContext.getSystemService(name)

}
inline fun <reified T : Any> View.getSystemService(): T = 
        context.getSystemService<T>()

inline fun <reified T : Any> Context.getSystemService(): T =
        getSystemService(T::class.java.name) as T

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment