Coder Social home page Coder Social logo

dfmdagger's Introduction

Build Status

dfmdagger

A small Android library that reduce the boilerplate code for your project when using Android Dagger with Android Dynamic Feature. This library allows you to use the Android Dagger (i.e @ContributesAndroidInjector) to resolve the dependencies in Dynamic Feature Module without the need of writing boilerplate code.

Background

This project is inspired by Dependency injection in a multi module project where the article shows how we can use Dagger to inject modules' components into the app component. While that works well for an Android project with Dynamic Feature Module, but I think it can be done with Android Dagger as well with some little tweaks.

The main difference here is that we use reflection to get the injector of a feature module and inject the feature module component into the application component before launching or accessing any classes in the feature module.

Usage

In app module

// MyApp.kt
class MyApp : DynamicFeatureModuleApplication() {
    override fun applicationInjector(): AndroidInjector<out DynamicFeatureModuleApplication> {
        return DaggerAppComponent.builder()
            .application(this)
            .build()
    }
}

// Create an extension function exposing appComponent() from this application class
fun Application.appComponent() = ((this as MyApp).component) as AppComponent

// AppModule.kt
@Module
class AppModule {
    // ...
}

// AppComponent.kt
@Singleton
@Component(
    modules = [
        AndroidInjectionModule::class,
        AppModule::class
    ]
)
interface AppComponent : AndroidInjector<MyApp> {
    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: Application): Builder
        
        fun build(): AppComponent
    }
}

// FeatureOneModule.kt
// Creating this class just to hold all the hard coded package/class names into 1 file
object FeatureOneModule : FeatureModule {
    override val injectorName = "com.my.package.featureone.FeatureOneInjector"
    
    // if you want to access the feature module's activity, implement AddressableActivity
    object FeatureOneActivity : AddressableActivity {
        override val packageName = "com.my.package"
        override val className = "com.my.package.featureone.FeatureOneActivity"
    }
    
    // if you want to access the feature module's fragment, implement AddressableObject
    object FeatureOneFragment : AddressableObject {
        override val className = "com.my.package.featureone.FeatureOneFragment"
    }
}

// MainActivity.kt
// Demonstrating how to launch other feature module
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // demo of launching another feature module activity
        startActivity(FeatureOneModule, intentTo(FeatureOneModule.FeatureOneActivity))
        
        // demo of launching another feature module fragment
        supportFragmentManager.beginTransaction()
            .replace(R.id.my_container, newFragment(FeatureOneModule.FeatureOneFragment))
            .commitNow(FeatureOneModule)
    }
}

In your feature module

// FeatureOneModule.kt
package com.my.package.featureone

@Module
class FeatureOneModule {
    // ...
}

// FeatureOneActivityModule.kt
@Module
abstract class FeatureOneActivityModule {
    @ContributesAndroidInjector
    abstract fun contributeFeatureOneActivity(): FeatureOneActivity
    
    @ContributesAndroidInjector
    abstract fun contributeFeatureOneFragment(): FeatureOneFragment
}

// FeatureOneComponent.kt
@FeatureScope
@Component(
    dependencies = [AppComponent::class],
    modules = [
        AndroidInjectionModule::class,
        FeatureOneActivityModule::class
    ]
)
interface FeatureOneComponent {
    fun inject(injector: FeatureOneInjector)
}

// FeatureOneInjector
// make sure this package name and class name are the same as you hard coded in the app module
package com.my.package.featureone

@Keep // adding this annotation so proguard/r8 will keep it for reflection purpose
class FeatureOneInjector : FeatureModuleInjector() {
    override fun inject(application: DynamicFeatureModuleApplication) {
        DaggerFeatureOneComponent.builder()
            .appComponent(application.appComponent())
            .build()
            .inject(this)
    }
}

// FeatureOneActivity.kt
package com.my.package.featureone

class FeatureOneActivity : DaggerActivity() {
    // ...
    @Inject lateinit var someDependency: SomeDependency
}

// FeatureOneFragment.kt
package com.my.package.featureone

class FeatureOneFragment : DaggerFragment() {
    // ...
    @Inject lateinit var someDependency: SomeDependency
}

Important Notes

  • If your feature module class name has changed, you must update all the hard coded class name in your app module as well.
  • The feature module will not be able to support @Singleton annotation. To have singleton in your feature module, you must make sure the class is singleton by default. You can achieve it using kotlin object MyClass or create the singleton pattern yourself.
  • The extension functions provided by this library (i,e startActivity(FeatureModule, Intent, Bundle?)) should satisfy most of the use cases where launching activity/fragment from other feature module. However, if you want to launch your feature module only to access its classes, you can inject the feature module injector first then use reflection to obtain the desired feature module class. For example:
// ServiceA.kt, must reside in `app` module, or a module that `app` has access to.
interface ServiceA {
    fun doSomething()
}

// In your feature module
// FeatureOneService.kt
package com.my.package.featureone

class FeatureOneService : ServiceA {
    override fun doSomething() {
        // ...
    }
}

// In your app module or other module that wants to access implementation of ServiceA
object FeatureOneServiceObject : AddressableObject {
    override val className = "com.my.package.featureone.FeatureOneService"
}

// Somewhere in your code where you want to access the implementation of ServiceA
// Note we specify the type as the interface name ServiceA instead of the implementation class
// FeatureOneService, this is because we might not have access to the FeatureOne module.
val serviceA = FeatureOneServiceObject.newInstance<ServiceA>()

// or
FeatureOneServiceObject.newInstance<ServiceA>() { serviceA -> serviceA.doSomething() }

Download

Step 1: Add the JitPack repository to your build file

Add this in your root build.gradle at the end of repositories:

allprojects {
    repositories {
	    ...
        maven { url 'https://jitpack.io' }
    }
}

Step 2: Add the dependency

dependencies {
    implementation 'com.github.sukhai:dfmdagger:VersionTag'
}

Current version is

License

Copyright (C) 2019 Su Khai Koh

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.

dfmdagger's People

Contributors

sukhai avatar

Stargazers

Kevendra Patidar avatar Michael avatar Harry Park avatar chonamdoo avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.