Kinject

KINJECT - Android/Java Dependency Injector.

View project on GitHub

Introduction

If you don´t know why dependency injection? You can first start here.

  • Why Kinject?

    Dependency injection frameworks are nothing new, in Android Dagger or more recently Dagger2 are well known examples (even some Kinject concepts are based on them).

    Kinject has been thought to get the following objectives:

    • Ease to configure.
    • Clean way to satisfy dependencies.
    • High performance without lose flexibility.
    • Integration little intrusive and easily abstractable.
    • Dependencies graph validation in compilation time.
    • Based on code generation.
    • Proguard friendly.

Using Kinject

The flow is simple, define your dependencies and inject them!

  • Defining dependencies

    Use an @Provides annotated method to define a dependency. The method’s return type define which dependency it satisfies.
    For example, provideHeater() is invoked whenever a Heater is required:

        @Provides
        public Heater provideHeater() {
          return new EletricalHeater();
        }
    

    Is possible for @Provides methods to have dependencies of their own.

        @Provides
        public Pump providePump(Heater heater) {
          return new Thermosiphon(heater);
        }
    

    All @Provides annotated methods must belong to a @Module annotated class.

        @Module
        public class HeaterModule {
    
          @Provides
          public Heater provideHeater() {
            return new ElectricalHeater();
          }
    
        }
    
  • Building the Graph

    Before you can inject dependencies, you must define and load a graph. You can understand a graph as a set of injectables dependencies (defined by @Provides annotation) at the same time, linked to other dependencies.
    In code, you have to set the graph root @Module annotation as completed = true.

    @Module(completed = true)
    public class CoffeeMakerModule {
    }
    

    You can lean on @Includes annotation to define more flexible graphs. With @Includes you can define modules componses by other modules. Each dependency defined in the included module will be part of the final graph.

    @Module(completed = true)
    public class CoffeeMakerModule {
    
      @Includes
      public HeaterModule includeHeaterModule() {
        return new HeaterModule();
      }
       
    }
    

    An implementation of ModuleMapper (Kinject needs it to load the graph), will be generated by each graph. That is named as the graph root module ended with Mapper.

    @Generated("com.wokdsem.kinject.codegen.CodeGen")
    public final class CoffeeMakerModuleMapper implements ModuleMapper {
          
      public static ModuleMapper from(CoffeeMakerModule coffeeMakerModule) {
      }
        
    }
    

    Now, you can initialize your injector and request dependencies.

    // Create injector
    ModuleMapper moduleMapper = CoffeeMakerModuleMapper.from(new CoffeeMakerModule());
    Kinject injector = Kinject.instantiate(moduleMapper);     
    
    // Injecting
    Heater heater = injector.get(Heater.class);
    
  • Aditional features

    • Singleton You can set singleton = true in @Provides annotation, so the injector instance will use a single instance of the dependency for all of its clients.
    @Provides(singleton = true)
    public Heater provideHeater() {
      return new ElectricalHeater();
    }
    
    • Named When the type isn’t enought to identify a dependency, you can set named field in @Provides annotation. By default, all dependencies have an empty named value.

      Use Kinject.get(Class<T> tClass, String named) to inject a named dependency.

      @Provides(named = "Application")
      public Context provideApplicationContext() {
      return appContext;
      }
      

Compile-Time

  • Validation

    The Kinject annotation processor throws a compiler error if any graph has an invalid state. For example, this completed module doesn’t know the Logger dependency.

    @Module(completed = true)
    public class CoffeeMakerModule {
        
      @Provides
      public void CoffeeMaker provideCoffeeMaker(Logger logger) {
        return new LogCoffeeMaker(logger);
      }
    
    }
    

    When compiling it, javac rejects something like:

      [[ KINJECT : ERROR ]] Unknown dependency(Logger@) on @Provides(CoffeeMaker@) building graph(CoffeeMakerModule).
    
  • Code Generation

    Kinject annotation processor generates source files with names ended like *Adapter.java or *Mapper.java. These files are Kinject implementation details and you shouldn’t edit them. Maybe they are usefull when you are debugging an injection.

Proguard

  • Kinject is Proguard friendly, you don’t need to do anything.

Additional android notes

  • Perhaps you would need to set up android.compileOptions.incremental = false in the app build file if you are having issues while compiling.

Installation

  • Status
    • Release version: 1.2.2
  • Download

    • You will need to include the kinject-${kinject.version}.jar in your application’s runtime.

    • In order to activate code generation, you will need to include kinject-compiler-${kinject.version].jar in your build at compile time.

    • In a gradle project, include the kinject artifact in the dependencies section of your build.gradle and the kinject-compiler artifact as provided dependency:
            dependencies {
              compile  'com.wokdsem.kinject:kinject:${kinject.version}'
              provided 'com.wokdsem.kinject:kinject-compiler:${kinject.version}'
            }
      
    • You can also find downloadable .jars on jCenter. Kinject / Kinject-Compiler.

License

Copyright 2016 Wokdsem

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.