gh-pages / wiki / Dependency Injection

Dependency Injection

object Dependency Injection (source)

DevFun supports a rudimentary form of dependency injection using an InstanceProvider (a CompositeInstanceProvider at DevFun.instanceProviders).

Dependency injection is used for:

Instance Providers

At runtime when an instance of an object is requested, DevFun.instanceProviders (a CompositeInstanceProvider loops through requesting an instance, starting from the most recently added (to allow users to add their own to be checked first). If the type cannot be provided, a ClassInstanceNotFoundException is thrown (will not crash the app - but it will be logged, and depending on the DevFunModule implementation will show an error).

Unless otherwise stated, each instance provider does a top-down search, using Class.isAssignableFrom. Most of the time this is sufficient - this could fail when complex inheritance hierarchies are used.

Inject Modules

At present only Dagger 2.x is explicitly supported (InjectFromDagger2). It is intended for Dagger 1.x and Guice to be supported eventually.

In short, it is a module that adds an instance provider. Your application (and its supers) are searched for any object that has an @Component annotation. This instance is then traversed for type providers or modules that provide the type. This may fail or be insufficient if your application has non-trivial object graphs.

The annotation Dagger2Component can also be used to tell DevFun how to get your components.

See InjectFromDagger2 for more details and limitations, or for details and examples on implementing your own.

Due to limitations in KAPT it is not possible to generate Kotlin code that would then generate dagger bindings.Once this has been resolved it should be possible to provide a more graceful implementation.

User Supplied Providers

Any object implementing InstanceProvider can be used. They can be added/removed at any time.

Add to DevFun.instanceProviders (Remove using -=.);

devFun.instanceProviders += myInstanceProvider

A utility function captureInstance can be used to quickly create an instance provider for a single type (creates a CapturingInstanceProvider). Be aware of leaks! The lambda could implicitly hold a local this reference. e.g.

class SomeType : BaseType

val provider = captureInstance { someObject.someType } // triggers for SomeType or BaseType

If you want to reduce the type range then specify its base type manually:

val provider = captureInstance<BaseType> { someObject.someType } // triggers only for BaseType

See DevFun.initialize for a source example. Other uses can be found throughout most module sources.

Default Providers

A number of providers are added by default, including all DevFun related types (modules, providers, etc.), some android related types (AndroidInstanceProvider), and support for Kotlin object types (KObjectInstanceProvider).

Android

Standard Android types are provided by AndroidInstanceProvider, which includes Application, Activity, Context, Fragment, and View.

At present only support fragments are supported.

Kotlin object

This is the second to last provider as it’s not reliable when the class is not public.

Constructable

This is the last provider to be checked, which can construct objects and inject constructors (using DevFun.instanceProviders).

In general you should use your own instance provider (or use one of the inject- modules, see Components), but for quick and dirty testing you can specify the type as Constructable. This tells the ConstructingInstanceProvider that it’s OK to create a new instance. The class can only have one constructor. Any arguments will be requested using DevFun.instanceProviders. (todo - allow @Constructable on constructors?)

This process is opt-in (i.e. @Constructable) to avoid issues where incorrect instance provider chains attempt to create new instances of something that should’ve been injected. The inject modules are simple and limited (at the moment) thus it is preferred to fail rather than silently creating new instances.