Reputation: 25267
So, I am trying to create an example, inspiring from GithubBrowserSample with Kotlin. I have successfully migrated to kotlin code but I getting error with my ApplicationComponent.kt
.
Error:(12, 2) error: [dagger.android.AndroidInjector.inject(T)] java.util.Map<java.lang.Class<? extends android.support.v4.app.Fragment>,javax.inject.Provider<dagger.android.AndroidInjector.Factory<? extends android.support.v4.app.Fragment>>> cannot be provided without an @Provides-annotated method.
public abstract interface ApplicationComponent {
^
java.util.Map<java.lang.Class<? extends android.support.v4.app.Fragment>,javax.inject.Provider<dagger.android.AndroidInjector.Factory<? extends android.support.v4.app.Fragment>>> is injected at
dagger.android.DispatchingAndroidInjector.<init>(injectorFactories)
dagger.android.DispatchingAndroidInjector<android.support.v4.app.Fragment> is injected at
com.chintansoni.android.architecturecomponentsblueprint.base.BaseActivity.dispatchingAndroidInjector
com.chintansoni.android.architecturecomponentsblueprint.view.activity.SplashActivity is injected at
dagger.android.AndroidInjector.inject(arg0)
I can see that issue is surfaced from AndroidInjectionModule from import dagger.android.AndroidInjectionModule
Let me share my code snippets.
KotlinApplication.kt
class KotlinApplication : Application(), HasActivityInjector {
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
override fun onCreate() {
super.onCreate()
initializeLogger()
initializeAppInjector()
}
private fun initializeAppInjector() {
AppInjector.init(this)
}
private fun initializeLogger() {
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
}
override fun activityInjector(): DispatchingAndroidInjector<Activity>? {
return dispatchingAndroidInjector
}
}
AppInjector.kt
object AppInjector {
fun init(kotlinApplication: KotlinApplication) {
DaggerApplicationComponent.builder()
.application(kotlinApplication)
.build()
.inject(kotlinApplication)
kotlinApplication.registerActivityLifecycleCallbacks(object : Application.ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
handleActivity(activity)
}
override fun onActivityStarted(activity: Activity) {
}
override fun onActivityResumed(activity: Activity) {
}
override fun onActivityPaused(activity: Activity) {
}
override fun onActivityStopped(activity: Activity) {
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
}
override fun onActivityDestroyed(activity: Activity) {
}
})
}
private fun handleActivity(activity: Activity) {
if (activity is HasSupportFragmentInjector) {
AndroidInjection.inject(activity)
}
(activity as? FragmentActivity)?.supportFragmentManager?.registerFragmentLifecycleCallbacks(
object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentCreated(fm: FragmentManager, f: Fragment,
savedInstanceState: Bundle) {
if (f is Injectable) {
AndroidSupportInjection.inject(f)
}
}
}, true)
}
}
ApplicationComponent.kt
@Singleton
@Component(modules = [(AndroidInjectionModule::class), (AppModule::class), (SplashActivityModule::class)])
interface ApplicationComponent {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: Application): Builder
fun build(): ApplicationComponent
}
fun inject(kotlinApplication: KotlinApplication)
}
SplashActivityModule.kt
@Module
abstract class SplashActivityModule {
@ContributesAndroidInjector(modules = [(FragmentBuildersModule::class)])
internal abstract fun contributeSplashActivity(): SplashActivity
}
BaseActivity.kt
abstract class BaseActivity : AppCompatActivity(), HasSupportFragmentInjector {
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Fragment>
override fun supportFragmentInjector(): DispatchingAndroidInjector<Fragment> {
return dispatchingAndroidInjector
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(getLayoutResource())
}
abstract fun getLayoutResource(): Int
}
My SplashActivity
extends BaseActivity
.
Upvotes: 0
Views: 950
Reputation: 25267
Everything was wired correctly. What I was doing wrong,
In ApplicationComponent
I included AndroidInjectionModule
which will only work to Inject Fragment from Android Framework.
Looking at the source of AndroidInjectionModule:
package dagger.android;
import android.app.Activity;
import android.app.Fragment;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ContentProvider;
import dagger.Module;
import dagger.internal.Beta;
import dagger.multibindings.Multibinds;
import java.util.Map;
/**
* Contains bindings to ensure the usability of {@code dagger.android} framework classes. This
* module should be installed in the component that is used to inject the {@link
* android.app.Application} class.
*/
@Beta
@Module
public abstract class AndroidInjectionModule {
@Multibinds
abstract Map<Class<? extends Activity>, AndroidInjector.Factory<? extends Activity>>
activityInjectorFactories();
@Multibinds
abstract Map<Class<? extends Fragment>, AndroidInjector.Factory<? extends Fragment>>
fragmentInjectorFactories();
@Multibinds
abstract Map<Class<? extends Service>, AndroidInjector.Factory<? extends Service>>
serviceInjectorFactories();
@Multibinds
abstract Map<
Class<? extends BroadcastReceiver>, AndroidInjector.Factory<? extends BroadcastReceiver>>
broadcastReceiverInjectorFactories();
@Multibinds
abstract Map<
Class<? extends ContentProvider>, AndroidInjector.Factory<? extends ContentProvider>>
contentProviderInjectorFactories();
private AndroidInjectionModule() {}
}
No mentions about Fragment from Support Library.
Now, if we look at the source of AndroidSupportInjectionModule:
package dagger.android.support;
import android.support.v4.app.Fragment;
import dagger.Module;
import dagger.android.AndroidInjectionModule;
import dagger.android.AndroidInjector;
import dagger.internal.Beta;
import dagger.multibindings.Multibinds;
import java.util.Map;
/**
* Configures bindings to ensure the usability of {@code dagger.android} and {@code
* dagger.android.support} framework classes. This module should be installed in the root-most
* component which will use these types.
*/
@Beta
@Module(includes = AndroidInjectionModule.class)
public abstract class AndroidSupportInjectionModule {
@Multibinds
abstract Map<Class<? extends Fragment>, AndroidInjector.Factory<? extends Fragment>>
supportFragmentInjectorFactories();
private AndroidSupportInjectionModule() {}
}
It includes support for injecting Fragment
from Support Library.
If you notice, AndroidSupportInjectionModule
also includes AndroidInjectionModule
. Hence, just including AndroidSupportInjectionModule
will work perfectly.
My BaseActivity was implementing HasSupportFragmentInjector
which only works with Support Fragment. There was no Module included into ApplicationComponent that can provide Fragment
dependency from Support Library, which was the main issue.
Upvotes: 0
Reputation: 7368
If you're using Fragment
from the support library, you've to use the HasSupportFragmentInjector
in your KotlinApplication
:
class KotlinApplication : Application(), HasActivityInjector, HasSupportFragmentInjector {
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
@Inject
lateinit var dispatchingFragmentInjector: DispatchingAndroidInjector<Fragment>
override fun onCreate() {
super.onCreate()
initializeLogger()
initializeAppInjector()
}
private fun initializeAppInjector() {
AppInjector.init(this)
}
private fun initializeLogger() {
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
}
override fun activityInjector(): DispatchingAndroidInjector<Activity>? {
return dispatchingAndroidInjector
}
override fun supportFragmentInjector(): AndroidInjector<Fragment>? {
return dispatchingFragmentInjector
}
}
also you've to install AndroidSupportInjectionModule
instead of AndroidInjectionModule
in your ApplicationComponent
:
@Singleton
@Component(modules = [(AndroidSupportInjectionModule::class), (AppModule::class), (SplashActivityModule::class)])
interface ApplicationComponent {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: Application): Builder
fun build(): ApplicationComponent
}
fun inject(kotlinApplication: KotlinApplication)
}
Upvotes: 1