Skip to content

TP 2 TP 3: Migration Guide

Stéphane Nicolas edited this page Aug 8, 2019 · 1 revision

To migrate from TP 2 to TP 3, here below are the most important changes. Note that we have also introduced new Toothpick Extensions.

Scope Annotations and Singleton Annotation

In TP 2, scopes annotations and singletons were entangled together. This confusion comes from the JSR 330. In TP 3, we interpreted the JSR differently in order to provide more flexibility and expressivity to DI.

In TP2, we typically used a scope annotation such as:

@ActivitySingleton class Foo {}

It meant that class Foo could only be instantiated in a scope that supported the ActivitySingleton annotation & that Foo will have a single instance (singleton) in such a scope.

In TP3, we offer a more fine grained distinction of the 2 concepts:

@ActivityScope class Foo {}

means that Foo can only be instantiated in a scope that supported the ActivitySingleton annotation. But a new instance will be created for each injection.

Independently, Foo can be annotated using @Singleton as well to indicate that Foo will have a singleton instance (singleton) of such a scope:

@ActivityScope @Singleton
class Foo {}

If the annotation @Singleton is used by itself, it designates a singleton of the root scope.

This new mechanism allows to enforce scope verification for virtually all injected components in an app.

We highly recommend that you rename your annotation to reflect this change. They should now end with the suffix Scope.

Scope Annotations and bindings

In TP 3, scope annotations and bindings have the same expressivity. The new binding DSL and a partial rewrite of TP internals now offer a 1:1 relationship between scope annotations and bindings.

The documentation, the conceptualization, and the code of TP 2 were not clear about the equivalence between scope annotations and bindings, and the binding API was inconsistent in several ways. TP 3 solves this. Bindings and Scope annotations offer exactly the same features, and they work complementarily.

scope annotations only

@ActivityScope class Foo

is exactly the same as defining:

 scope.supportScopeAnnotation(ActivityScope::class)
      .installModule( module { 
        bind<Foo>()
      })

scope singletons

@ActivityScope @Singleton class Foo

is exactly the same as defining:

 scope.supportScopeAnnotation(ActivityScope::class)
      .installModule( module { 
        bind<Foo>().singleton()
      })

releasable singletons

@ActivityScope @Singleton @Releasable class Foo

is exactly the same as defining:

 scope.supportScopeAnnotation(ActivityScope::class)
      .installModule( module { 
        bind<Foo>().singleton().releasable()
      })

for providers

@ActivityScope @Singleton
@ProvidesSingleton @ProvidesReleasable 
class FooProvider implements Provider<Foo>

is exactly the same as defining:

 scope.supportScopeAnnotation(ActivityScope::class)
      .installModule( module { 
        bind<Foo>().toClass<FooProvider>()
                   .providesSingleton()
                   .providesReleasable()
                   .singleton()
      })

Mixing annotations and bindings

Note that it is possible to mix annotations and bindings information. The resulting behavior is to "add" all the behaviors expressed by both annotations and bindings.

Scope opening API

In TP 2, to open a branch in the scope tree, we typically did something like

Toothpick.openScopes(application, activity);

In TP 3, we prefer the more fluent API (available for java & kotlin):

KTP.openScope(application)
   .openSubScope(activity)

In TP 3, we have introduced a new lambda to configure a scope when it is created (the lambda is not used when the scope was previously opened)

KTP.openScope(application) { it.installModule( module {...} ) }
   .openSubScope(activity) { it.installModule( module {...} ) }

Closing scopes automatically

Scopes can now follow an activity or fragment or viewmodel lifecyle, see Toothpick Extensions

Scope Fluent API

In TP 3, the Scope API is now fluent and allows to chain calls in a more elegant way:

KTP.openScope(application) { ... }
   .openSubScope(activity) { ... }
   .supportScopeAnnotation(ActivityScope::class)
   .inject(this)

Releasable singletons

In TP 3, Toothpick can now release all singletons that are marked as releasable, either by binding or annotations.

KTP.openScope(application).release()

will release all singletons marked as releasable: making them ready for garbage collection. This is typically used when your app is under memory pressure.

New Annotations

In TP 3, we have introduced new annotations:

  • @ProvidesSingleton is used to annotate a provider class and indicates that the instance provided will be a singleton.
  • @ProvidesReleasable can only be used with @ProvidesSingleton, it is used to annotate a provider class and indicates that the instance provided will be a releasable singleton.
  • @Releasable can only be used with @Singleton, it is used to annotate a class and indicates that the instance will be a releasable singleton.
  • @InjectConstructor can be used to annotate a class, it's equivalent to annotate its primary constructor with @Inject.

And more kotlin!

As you can see from the above examples there is a new Kotlin API. You can find more here: KTP

Clone this wiki locally