I am trying to make dagger-android work with Conductor (or any custom class). I tried replicating everything that AndroidSupportInjectionModule (and friends) do, which in my mind is the same kind of a custom class handling.
However I get
C:\Users\ursus\AndroidStudioProjects\...\ControllersModule.java:15: error: com.foo.bar.ChannelsController is not a framework type
public abstract com.foo.bar.ChannelsController channelsController();
So, my "library" code
package com.foo.bar
import com.bluelinelabs.conductor.Controller;
import dagger.Module;
import dagger.android.AndroidInjectionModule;
import dagger.android.AndroidInjector;
import dagger.internal.Beta;
import dagger.multibindings.Multibinds;
import java.util.Map;
@Beta
@Module(includes = AndroidInjectionModule.class)
public abstract class ConductorInjectionModule {
private ConductorInjectionModule() {
}
@Multibinds
abstract Map<Class<? extends Controller>, AndroidInjector.Factory<? extends Controller>> controllerInjectorFactories();
@Multibinds
abstract Map<String, AndroidInjector.Factory<? extends Controller>> controllerInjectorFactoriesWithStringKeys();
}
I dont even get compiled, so presuming pasting ConductorInjection & HasControllerInjector is pointless
Usage:
@Module
abstract class AppModule {
@ContributesAndroidInjector abstract fun mainActivity(): MainActivity
@ContributesAndroidInjector abstract fun channelsController(): ChannelsController
}
class App : Application(), HasActivityInjector, HasControllerInjector {
@Inject lateinit var activityInjector: DispatchingAndroidInjector<Activity>
@Inject lateinit var controllerInjector: DispatchingAndroidInjector<Controller>
private lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent.builder()
.applicationContext(this)
.build()
.apply {
inject(this@App)
}
}
override fun activityInjector() = activityInjector
override fun controllerInjector() = controllerInjector
}
@Singleton
@Component(
modules = [
AndroidInjectionModule::class,
ConductorInjectionModule::class,
AppModule::class,
NetModule::class
]
)
interface AppComponent {
fun inject(app: App)
@Component.Builder
interface Builder {
@BindsInstance
fun applicationContext(context: Context): Builder
fun build(): AppComponent
}
}
implementation deps.dagger.runtime
implementation deps.dagger.androidRuntime
kapt deps.dagger.compiler
kapt deps.dagger.androidCompiler
where it is all "2.19" version (have tried 2.16)
AGP "com.android.tools.build:gradle:3.3.0-rc02" (have tried 3.2.1 stable)
Any clue? In my mind it all should work as its the same thing dagger-android-support
does
error: com.foo.bar.ChannelsController is not a framework type
So the question to answer is, "how does dagger-android know what a framework type is or not".
The answer can be found in this commit to Dagger-Android between 2.19 and 2.20, where they "removed the old way of doing things for better compatibility with AndroidX".
So as we can see in https://stackoverflow.com/a/53891780/2413303 ,
/** * Returns the Android framework types available to the compiler, keyed by their associated {@code * dagger.android} {@link MapKey}s. This will always contain the types that are defined by the * framework, and only contain the support library types if they are on the classpath of the * current compilation. */ static ImmutableMap<Class<? extends Annotation>, TypeMirror> frameworkTypesByMapKey( Elements elements) { return ImmutableMap.copyOf( Stream.of( elements.getPackageElement("dagger.android"), elements.getPackageElement("dagger.android.support")) .filter(packageElement -> packageElement != null) .flatMap(packageElement -> typesIn(packageElement.getEnclosedElements()).stream()) .filter(AndroidMapKeys::isNotAndroidInjectionKey) .filter(type -> isAnnotationPresent(type, MapKey.class)) .filter(mapKey -> mapKey.getAnnotation(MapKey.class).unwrapValue()) .flatMap(AndroidMapKeys::classForAnnotationElement) .collect(toMap(key -> key, key -> mapKeyValue(key, elements)))); }
they had code that checked their own @MapKey
types in dagger.android
and dagger.android.support
packages, that looked like this:
// java/dagger/android/support/FragmentKey.java
@Beta
@MapKey
@Documented
@Target(METHOD)
@Deprecated
public @interface FragmentKey {
Class<? extends Fragment> value();
}
So they read the framework types based on what @MapKey
s were available in the dagger.android
and dagger.android.support
packages.
Apparently they removed this check in 2.20 so now you can inject whatever you want. Rejoice!
But otherwise you could actually hack it in such a way that you'd add a @ControllerKey
and a @ViewKey
in dagger.android
package in your project, and it'd actually likely work with 2.19.
The tests that were checking for errors in "is not a framework type" are also removed in that commit.
Ah, and
@Multibinds
abstract Map<String, AndroidInjector.Factory<? extends Controller>> controllerInjectorFactoriesWithStringKeys();
You can remove this part too with 2.20, all you need now is AndroidInjectionModule
.
For future travelers, they were hardcoding some appcompat stuff in the annot. processor, thats why appcompat fragments worked.
update to dagger 2.20, it will magically work
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With