cashapp / inflationinject Goto Github PK
View Code? Open in Web Editor NEWConstructor-inject views during XML layout inflation
License: Apache License 2.0
Constructor-inject views during XML layout inflation
License: Apache License 2.0
Currently it's an error. Warning should say you can inject Foo
or Provider<Foo>
without a factory.
It would be great if AssistedInject could manage things like this:
class Foo @Inject constructor(activity: Activity, otherDependency: Other)
class Bar @AssistedInject constructor(foo: Foo) {
@AssistedInject.Factory
interface Factory {
fun create(activity: Activity)
}
}
I need to inject something but I'm missing one dependency (or more) in the graph. So I provide it using an @AssistedInject.Factory
. The difference here is that the missing dependency is not injected directly to the class that exposes the Factory.
Build project is a success but AssitedInject class doesn't create and I can't start a project. Please help!
I have separate classes:
module:
@Module(includes = {AssistedInject_AssistedInjectModule.class}) @AssistedModule public interface AssistedInjectModule { }
Component:
@Component(modules = {AssistedInjectModule.class, WorkerBindingModule.class}) public interface WorkerComponent { WorkerFactory factory(); }
Child interface:
public interface ChildWorkerFactory { ListenableWorker create(Context context, WorkerParameters workerParameters); }
Worker binding module:
@Module public interface WorkerBindingModule { @Binds @IntoMap @WorkerKey(UIUpdaterWorker.class) ChildWorkerFactory bindUIUpdaterWorker(UIUpdaterWorker.Factory factory); }
Worker key:
@MapKey @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface WorkerKey { Class<? extends ListenableWorker> value(); }
Worker factory:
public class DaggerWorkerFactory extends WorkerFactory { private Map<Class<? extends ListenableWorker>, Provider<ChildWorkerFactory>> workersFactories; @Inject public DaggerWorkerFactory(Map<Class<? extends ListenableWorker>, Provider<ChildWorkerFactory>> workersFactories) { this.workersFactories = workersFactories; } @Nullable @Override public ListenableWorker createWorker(@NonNull Context appContext, @NonNull String workerClassName, @NonNull WorkerParameters workerParameters) { Provider<ChildWorkerFactory> factoryProvider = CollectionsUtil.getWorkerFactoryProviderByKey(workersFactories, workerClassName); return factoryProvider.get().create(appContext, workerParameters); } }
There is my worker: (DBmodel is injected class from appComponent)
public class UIUpdaterWorker extends Worker { private DBModel dbModel; String TAG = "FIRST_LAUNCH"; @Inject public UIUpdaterWorker(@Assisted @NonNull Context context,@Assisted @NonNull WorkerParameters workerParams, DBModel dbModel) { super(context, workerParams); this.dbModel = dbModel; } @NonNull @Override public Result doWork() { Log.d(TAG, "Hello world!"); Log.d(TAG, "Injected foo: " + dbModel); return Result.success(); } @AssistedInject.Factory interface Factory extends ChildWorkerFactory{} }
Part in application class:
private void configureWorkManager() { DaggerWorkerFactory workerFactory = new DaggerWorkerComponent.builder().factory(); WorkManager.initialize(this, new Configuration.Builder().setWorkerFactory(workerFactory).build()); }
Part of gradle: (version_work is 1.0.1)
compileOnly "com.squareup.inject:assisted-inject-annotations-dagger2:0.3.2" annotationProcessor 'com.squareup.inject:assisted-inject-processor-dagger2:0.4.0' implementation "android.arch.work:work-runtime:$versions_work" implementation "android.arch.work:work-rxjava2:$versions_work"
AssistedInject should support Gradle incremental AP: https://docs.gradle.org/nightly/userguide/java_plugin.html#sec:incremental_annotation_processing
All AP that we use in our project already supports incremental AP except AssistedInject so it's impossible to have incremental AP until all AP in your project support it
With test. I have this working in a side-project, but we should show it here and write a test to make sure it continues working.
I was trying to create an assisted module for my viewmodel but it doesn't generate. This is my viewmodel:
class GalleryViewModel @AssistedInject constructor(
val galleryDataSourceFactory: GalleryDataSourceFactory
) : ViewModel() {
@AssistedInject.Factory
interface Factory {
fun create(): GalleryViewModel
}
}
And it compiles with no errors. Perhaps i should include the generated factory:
public final class GalleryViewModel_Factory implements Factory<GalleryViewModel> {
private final Provider<GalleryDataSourceFactory> galleryDataSourceFactoryProvider;
public GalleryViewModel_Factory(
Provider<GalleryDataSourceFactory> galleryDataSourceFactoryProvider) {
this.galleryDataSourceFactoryProvider = galleryDataSourceFactoryProvider;
}
@Override
public GalleryViewModel get() {
return new GalleryViewModel(galleryDataSourceFactoryProvider.get());
}
public static GalleryViewModel_Factory create(
Provider<GalleryDataSourceFactory> galleryDataSourceFactoryProvider) {
return new GalleryViewModel_Factory(galleryDataSourceFactoryProvider);
}
public static GalleryViewModel newInstance(GalleryDataSourceFactory galleryDataSourceFactory) {
return new GalleryViewModel(galleryDataSourceFactory);
}
}
Last, when I try the @AssistedModule, I can't find my assistedmodule:
@AssistedModule
@Module(includes = [AssistedInject_ViewModelModule::class]) //not generating
abstract class ViewModelModule
I'm using latest version of the lib
In a non-general way, the cases are:
This prevents someone else from re-using the annotation for something else though. Maybe we're fine with that to start with until someone complains?
We've now hit the rule of three with public use cases for this feature:
@ContributesAndroidInjector
@AssistedInject
@InflationInject
The first one has massively superior ergonomics because its generated bindings can be auto-discovered and bound into the graph. It gets this by virtue of being built by the Dagger team. Lucky!
The latter two are in this repo and do not receive auto-discovery. Instead, we force the consumers to annotate a module such that we generate a sibling module which contains only unscoped bindings. This lets us talk to Dagger where we otherwise cannot. It has devastating effects on incremental compilation and incremental annotation processing since both need to aggregate all usages in a compilation unit. And finally, it has annoying ergonomics because you have to reference generated code directly and manually include a module.
An additional hypothetic use case as well as the general Dagger feature request is tracked here: google/dagger#869. This also would eliminate the need for Dagger to build everything as a first-party feature (e.g., google/dagger#935) and allow people to mix-in behavior from other libraries.
We should come up with a proposal for the general mechanism on which Dagger's @ContributesAndroidInjector
and our @AssistedInject
and @InflationInject
can be built.
Allow generated factories to implement parameterized FactoryTypes.
This is useful for annotation processors that use AssistedInjection like InflationInjectProcessor.
Example: ViewModelInject
https://github.com/hansenji/ViewModelInject/blob/master/viewmodel-inject-processor/src/main/java/com/vikingsen/inject/viewmodel/processor/ViewModelInjectProcessor.kt#L228
ViewModel:
class FeedDetailsViewModel @AssistedInject constructor(
@Assisted private val feedId: FeedId,
private val getFeedUseCase: GetFeedUseCase
) : BaseViewModel() {
...
@AssistedInject.Factory
interface Factory {
fun create(feedId: FeedId): FeedDetailsViewModel
}
}
Generated factory:
public final class FeedDetailsViewModel_AssistedFactory implements FeedDetailsViewModel.Factory {
private final Provider<GetFeedUseCase> getFeedUseCase;
@Inject
public FeedDetailsViewModel_AssistedFactory(Provider<GetFeedUseCase> getFeedUseCase) {
this.getFeedUseCase = getFeedUseCase;
}
@Override
public FeedDetailsViewModel create(FeedId feedId) {
return new FeedDetailsViewModel(
feedId,
getFeedUseCase.get());
}
}
Module:
@Module
interface FeedDetailsActivityModuleKotlin {
@Binds
fun viewModelFactory(factory: FeedDetailsViewModel_AssistedFactory): FeedDetailsViewModel.Factory
}
Build fails with the following error:
e: /app/build/tmp/kapt3/stubs/debug/../FeedDetailsActivityModuleKotlin.java:11: error: @Binds methods must have only one parameter whose type is assignable to the return type
public abstract com.nikolaykul.sebastian.presentation.feed.details.FeedDetailsViewModel.Factory viewModelFactory(@org.jetbrains.annotations.NotNull()
A few notes:
@Module
public interface FeedDetailsActivityModuleJava {
@Binds
FeedDetailsViewModel.Factory viewModuleFactory(FeedDetailsViewModel_AssistedFactory factory);
}
@AssistedModule
builds fine as well. I guess thats because it generates java-code:@AssistedModule
@Module(includes = [AssistedInject_ViewModelModule::class])
interface ViewModelModule
How can I fix it? ๐ค
interface BaseThing<T> {
interface Factory {
fun create(view: View): BaseThing<T>
}
}
class StringThing @AssistedInject constructor (
@Assited view: View
): BaseThing<String> {
@AssistedInject.Factory
interface Factory : BaseThing.Factory<String>
}
This will not generate an implementation for the factory, but a generated Dagger 2 module will still try to reference the not generated factory.
Adding override fun create(view: View): BaseThing<String>
to the Factory will fix it.
Build log:
DetailsViewModelModule.java:9: error: This module is public, but it includes non-public (or effectively non-public) modules. Either reduce the visibility of this module or make app.tivi.showdetails.AssistedInject_DetailsViewModelModule public.
public abstract class DetailsViewModelModule {
^
The module is just a plain Kotlin public class:
@AssistedModule
@Module(includes = [AssistedInject_DetailsViewModelModule::class])
abstract class DetailsViewModelModule
The generated class is pkg-private so I can't see a way to make it work in Kotlin
Seems like it would be better to have the factory interfaces be autogenerated rather than require the boilerplate of declaring an interface (which in turn is mostly a duplicate of the constructor).
When the target class has a Provider<T>
binding, the generated factory class includes a constructor param Provider<Provider<T>>
. As a result, Dagger 2 can't find the binding.
Should AssistedInject flatten Provider<T>
bindings in this case?
Seems InflationInject only binds the view factory but doesn't provide the view, I ran into a use case that I need to constructor inject a view like:
class ProductView @InflationInject constructor(
@Assisted context: Context,
@Assisted attrs: AttributeSet? = null,
imageLoader: ImageLoader
) : View(context, attrs)
class ProductListView @InflationInject constructor(
@Assisted context: Context,
@Assisted attrs: AttributeSet? = null,
productViewProvider: Provider<ProductView>
) : View(context, attrs)
The build fails, saying "ProductView cannot be provided without an @Inject constructor or an @Provides-annotated method."
Mostly just an unfortunate side-effect of re-using code gen. Still works fine.
We probably have to split the model into two: one which represents the elements and the other which represents the JavaPoet names + keys. This way each processor can have separate element models for validation but then both produce a name+key model for code gen.
Hello,
let's say we have objects with different scopes that are annotated using @AssistedInject
. For now we're limited to have one @AssistedModule
per module. In that case it force us to split scoped objects into different modules (because of missing binding error in parent component). Are there any plans to change that behavior and allow to have multiple @AssistedModule
in one module?
There is an @Ignored
test for this.
How do I make Dagger AssistedModule generate bindings for @AssistedInjected classes in different Android modules?
When I check the generated module, I dont see any bindings for the generated factories in the other module.
class Thing {
private final String one;
private final int two;
private final String three;
@AssistedInject
Thing(String one, int two, @Assisted String three) {
this.one = one;
this.two = two;
this.three = three;
}
String get() {
return one + two + three;
}
@AssistedInject.Factory
interface Factory {
Thing create(String three);
}
}
Caused by: java.lang.IllegalArgumentException: invalid type parameter: int
at com.squareup.javapoet.Util.checkArgument(Util.java:53)
at com.squareup.javapoet.ParameterizedTypeName.<init>(ParameterizedTypeName.java:51)
at com.squareup.javapoet.ParameterizedTypeName.<init>(ParameterizedTypeName.java:38)
at com.squareup.javapoet.ParameterizedTypeName.get(ParameterizedTypeName.java:114)
at com.squareup.inject.assisted.processor.AssistedInjectionKt.getProviderType(AssistedInjection.kt:101)
at com.squareup.inject.assisted.processor.AssistedInjectionKt.access$getProviderType$p(AssistedInjection.kt:1)
at com.squareup.inject.assisted.processor.AssistedInjection.brewJava(AssistedInjection.kt:61)
at com.squareup.inject.assisted.processor.AssistedInjectProcessor.writeAssistedInject(AssistedInjectProcessor.kt:240)
at com.squareup.inject.assisted.processor.AssistedInjectProcessor.access$writeAssistedInject(AssistedInjectProcessor.kt:46)
at com.squareup.inject.assisted.processor.AssistedInjectProcessor$process$3.invoke(AssistedInjectProcessor.kt:65)
at com.squareup.inject.assisted.processor.AssistedInjectProcessor$process$3.invoke(AssistedInjectProcessor.kt:46)
at com.squareup.inject.assisted.processor.AssistedInjectProcessor$sam$java_util_function_BiConsumer$0.accept(AssistedInjectProcessor.kt)
at com.squareup.inject.assisted.processor.AssistedInjectProcessor.process(AssistedInjectProcessor.kt:65)
at org.gradle.api.internal.tasks.compile.processing.DelegatingProcessor.process(DelegatingProcessor.java:62)
at org.gradle.api.internal.tasks.compile.processing.NonIncrementalProcessor.process(NonIncrementalProcessor.java:45)
at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:964)
at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs(JavacProcessingEnvironment.java:881)
at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.access$2100(JavacProcessingEnvironment.java:110)
at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run(JavacProcessingEnvironment.java:1202)
at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing(JavacProcessingEnvironment.java:1311)
at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.processAnnotations(JavaCompiler.java:1250)
at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:928)
at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.lambda$doCall$0(JavacTaskImpl.java:100)
at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.handleExceptions(JavacTaskImpl.java:142)
... 65 more
Since this is a single interface with a single implementation, R8 should be able to vertically merge these together and eliminate the interface even through Dagger's generated machinery. If it can't, figure out why and forward to the R8 team.
This will only work for assisted injection and not inflation inject since the latter is used in multibindings which requires the interface as an abstraction.
This issue will be visible when using Gradle's remote build cache. The following screenshots are from Android Studio and a Gradle Enterprise instance used by a team at Comcast, with whom I work (I am a Gradle employee).
The tl;dr is that, given a set of inputs, this annotation processor seems to generate code with non-deterministic output (the order of methods in the generated code is what is non-deterministic). It's a classic indication of HashMap usage (or similar).
File diff (CI vs local dev build):
Gradle Enterprise task inputs comparison showing the file input that was "different" and,
therefore, an issue for the remote build cache:
Gradle Enterprise infrastructure comparison showing the different JDKs in use:
As I said, this is a classic sign of using something like a HashMap as the data structure, because of course it has no defined order, and so different JDKs will have different implementations.
Observation
Generated code has non-deterministic order with respect to JDK in use
Expectation
Generated source code is reproducible no matter where it is generated.
These cannot be specified in XML so they won't match.
We should add integration tests for this.
This code:
class MyClass @AssistedInject constructor(
@Foo foobar1: Foobar,
@Foo foobar2: Foobar,
@Assisted a: String
) {
@AssistedInject.Factory
interface Factory {
fun create(a: String): MyClass
}
}
Produces this error:
Duplicate non-@assisted parameters declared. Forget a qualifier annotation?
It seems like this is an unnecessary restriction. I acknowledge that it may be an uncommon use case, but it seems reasonable that I might want to construct a MyClass
manually in a unit test with different values for foobar1
and foobar2
.
In my particular case, it's 2 RxJava Scheduler
args. When constructed via Dagger, I expect to receive the same object twice (a main thread scheduler). In my unit test, I use one of them for a timer, so I supply a TestScheduler
, and the other for observeOn
the main thread, so I supply Schedulers.trampoline()
. The above error means I cannot supply the separate Scheduler
s, which in turn means I must triggerActions()
on my TestScheduler
in order to execute the majority of my tests.
Can this restriction be lifted? Is it truly necessary, or a case of failing eagerly to detect programmer error?
Use auto common to figure out which one. Thanks, Obama Java.
Similar to #95.
> Task :app:kaptInternalDebugAndroidTestKotlin
Incremental annotation processing requested, but support is disabled because the following processors are not incremental: com.squareup.inject.inflation.processor.InflationInjectProcessor (NON_INCREMENTAL).
Hello.
I want to use lifecycle-viewmodel-savedstate in my project, so I came across AssistedInject and decided to use it.
But regardless of all my efforts to mix it with my current project stucture I get compiler errors all the time.
Everything compiles and works fine if I don't use @AssistedInject, @AssistedInject.Factory and ViewModelAssistedFactoriesModule.
But as a result I obviously can't use "@assisted SavedStateHandle savedStateHandle" which was the main goal of all this.
So is it possible to use @AssistedInject with all this @ContributesAndroidInjector and @AuthScope stuff?
Here is my simple configuration:
@Singleton
@Component(modules = [AndroidSupportInjectionModule::class,
AppModule::class,
ActivityBuildersModule::class,
ViewModelFactoryModule::class,
ViewModelAssistedFactoriesModule::class])
interface AppComponent : AndroidInjector<App> {
@Component.Builder
interface Builder {
@BindsInstance
fun context(context: Context): Builder
fun build(): AppComponent
}
}
AppModule provides global @singleton dependencies like, for example, Context and so on.
@Module
internal abstract class ActivityBuildersModule : BaseActivityBuildersModule() {
@MainScope
@ContributesAndroidInjector(modules = [MainFragmentBuildersModule::class, MainViewModelsModule::class, MainModule::class])
internal abstract fun contributeMainActivity(): MainActivity
}
@Module
public abstract class BaseActivityBuildersModule {
@AuthScope
@ContributesAndroidInjector(
modules = {AuthViewModelsModule.class, AuthModule.class} //this is important, because AuthModule provides @AuthScope scoped dependencies
)
abstract AuthActivity contributeAuthActivity();
}
@Module
public abstract class AuthViewModelsModule {
@Binds
@IntoMap
@ViewModelKey(AuthActivityVM.class)
public abstract ViewModelAssistedFactory<? extends ViewModel> bindAuthActivityViewModel(AuthActivityVM.Factory viewModelFactory);
@Binds
abstract SavedStateRegistryOwner bindSavedStateRegistryOwner(AuthActivity activity);
@Nullable
@Provides
static Bundle provideDefaultArgs() {
return null;
}
}
public interface ViewModelAssistedFactory<T extends ViewModel> {
T create(SavedStateHandle savedStateHandle);
}
@Module
public class AuthModule {
@Provides
@AuthScope
static LoginValidator provideLoginValidator(){
return new AuthValidator();
}
//
//and other @AuthScope things
//
}
@Module
abstract class ViewModelFactoryModule {
@Binds
abstract fun bindViewModelFactory(viewModelFactory: ViewModelProviderFactory): ViewModelProvider.Factory
}
@AssistedModule
@Module(includes = {AssistedInject_ViewModelAssistedFactoriesModule.class})
public abstract class ViewModelAssistedFactoriesModule {
}
public class AuthActivityVM extends ViewModel {
@AssistedInject.Factory
public interface Factory extends ViewModelAssistedFactory<AuthActivityVM>{}
@AssistedInject
//@Inject
AuthActivityVM(@Assisted SavedStateHandle savedStateHandle,
@NonNull Context context, //no complier complains
... //other @Singleton dependencies (also fine)
@NonNull LoginValidator loginValidator, **//loginValidator comes from AuthModule and it is scoped with @AuthScpoe**
...// oter things
){
...
}
}
In AuthActivity I have this bit of code:
@Inject
ViewModelProviderFactory providerFactory;
But compiler complains:
com.alidi.base.models.auth.LoginValidator cannot be provided without an @Provides-annotated method.
javax.inject.Provider<com.alidi.base.models.auth.LoginValidator> is injected at
com.alidi.base.ui.auth.AuthActivityVM_AssistedFactory(โฆ, loginValidator, โฆ)
:)
Return type of the factory's create method that are assignable to the factory'd type should be allowed
class Test extends TestBase {
Test(Long foo, @Assisted String bar) {}
@Assisted.Factory
interface Factory {
TestBase create(String bar);
}
}
Currently the generated AssistedInject_*Module contains all the bindings for @AssistedInject.Factory
annotated factories in one module. As a result, it's impossible to have dependencies that exist only in subcomponents. For example:
I have two ViewModels, where one of them takes an argument (GithubApi
in this case), which exists only in one the subcomponent hosting these view models.
class CommitDetailViewModel @AssistedInject constructor(
@Assisted handle: SavedStateHandle,
privatel val githubApi: GithubApi // Should be provided only in CommitDetailActivitySubcomponent
) : BaseViewModel(handle) {
fun loadDetail() {
Timber.v("Request commit detail")
// use githubApi here
}
@AssistedInject.Factory
interface Factory : ViewModelAssistedFactory<CommitDetailViewModel>
}
class CommitListViewModel @AssistedInject constructor(
@Assisted handle: SavedStateHandle
) : BaseViewModel(handle) {
fun loadCommits() {
Timber.v("Request commit list")
}
@AssistedInject.Factory
interface Factory : ViewModelAssistedFactory<CommitListViewModel>
}
interface ViewModelAssistedFactory<T : ViewModel> {
fun create(handle: SavedStateHandle): T
}
@AssistedModule
@Module(includes = AssistedInject_ViewModelAssistedFactoriesModule.class)
public abstract class ViewModelAssistedFactoriesModule {
}
The generated AssistedInject_ViewModelAssistedFactoriesModule
contains the bindings for both factories above. Due to that, I have to include it in the ApplicationComponent
so it's visible to my subcomponents. But then compilation fails since GithubApi
's binding is only defined in one of the subcomponents.
Summary: Currently, the AssistedInject compiler does not inform the user whether the name/type mismatch occurs at the interface/factory or during usage in the assist-injected constructor.
Example:
interface ListenableWorkerFactory {
fun create(context: Context, params: WorkerParameters): RxWorker
}
Constructor Usage:
class ConversationOperationWorker
@AssistedInject constructor(@Assisted context: Context,
@Assisted params2: WorkerParameters, //note, should be `params`
private val client: ConversationActionClient,
private val gson: Gson) : RxWorker(context, params2){ //note, should be `params`
@AssistedInject.Factory
interface Factory : ListenableWorkerFactory
//... body
}
The compiler error's stacktrace was as follows:
e: error: Factory method parameters do not match constructor @Assisted parameters.
Missing:
* android.content.Context context
* androidx.work.WorkerParameters params2
Unknown:
* android.content.Context arg0
* androidx.work.WorkerParameters arg1
Although the compiler was correct in that the ListenableWorkerFactory
's params were not being matched, it did not tell me where they weren't being matched.
Now, admittedly, there was user-error here, and it was twofold:
params2
)That being said, having the compiler point to the erroneous usage would be nice:
the error message should pass the offending element so that javac
highlights it like a compiler error, with the little snippet + shark-fin ^^ underneath.
Noting at which site the element resides (e.g. constructor vs. factory vs. inherited factory interface), so that the error would say something like "${paramName} Declared in constructor but missing from factory"
or "${paramName} present in factory but but missing from constructor"
This happens on the latest 0.5.0
binary.
Cubby is a great place to work
There are @Ignored
tests for this.
AssistedInject fails without explanation when I try to use it with various classes that use generics. The simplest case that fails for me with Dagger 2.24, AssistedInject 0.5.0 and Kotlin 1.3.50:
class AClass<A> @AssistedInject constructor(
@Assisted dependency: String
) {
@AssistedInject.Factory
interface Factory {
fun create(dependency: String): AClass<String>
}
}
The only error that appears is the following:
error: cannot find symbol
@dagger.Module(includes = {AssistedInject_PresentationModule.class})
^
symbol: class AssistedInject_PresentationModule
FAILURE: Build failed with an exception.
This is just a simplified version of what I was trying to do initially, which also fails with no meaningful message:
abstract class Base<T>
class Wrapper<A, B>
class AClass<A, B> @AssistedInject constructor(
@Assisted dependency: (A) -> B
) : Base<Wrapper<A, B>>() {
@AssistedInject.Factory
interface Factory {
fun <A, B> create(dependency: (A) -> B): AClass<A, B>
}
}
Annotating multiple modules with @AssistedModule
results in error: Multiple @AssistedModule-annotated modules found
. After watching your talk from DroidCon UK '18, where you mention that there should be one (and only one?) module that is annotated with @AssistedModule
in the whole project, it all became clear to me and I managed to fix this error eventually.
At least in the current version of the library, this rule isn't mentioned (or at least unclear) in the README.md and/or the sample. Even though this behavior might change in the future, and it would be allowed to annotate multiple modules, I think it's still worth mentioning it - at least for the current behavior of the library.
There are @Ignore
d tests for this in both assisted and inflation inject tests.
Getting this error the first time I compile each time:
e: error: Factory method parameters do not match constructor @Assisted parameters.
Missing:
* androidx.lifecycle.SavedStateHandle handle
Unknown:
* androidx.lifecycle.SavedStateHandle arg0
The argument name does actually match, and this error goes away upon building a second time. Not sure interaction is causing this.
If I have a constructor annotated with AssistedInjection with no non-assisted parameter I get this error:
error("Assisted injection requires at least one non-@assisted parameter.", targetConstructor)
Is this some technical limitation or it's only a rule applied.
I know it doesn't make much sense but what I'm trying to do is to establish some framework to inject ViewModels into fragments. I want the ViewModelProvider Factories (and then ViewModels) have injected by dagger. But usually there is some Assisted parameter so I'm using AssistedInjection Factories to provide that.
There can however be a VM without non-assisted parameters but I still want it to be injected the same way the others are. But with these VMs I'm getting this error.
I'm trying to make AssistedInject work with dagger and kotlin but no success so far.
It seems dagger generates module classes before assisted factories so it ends up with a error NonExistentClass
from the factory binding.
@Binds abstract fun bindHomeAssistedInjectFactory(factory: HomeAssistedInject_AssistedFactory): HomeAssistedInject.Factory
generates :
@org.jetbrains.annotations.NotNull() @dagger.Binds() public abstract HomeAssistedInject.Factory bindHomeAssistedInjectFactory(@org.jetbrains.annotations.NotNull() NonExistentClass factory);
I remember trying with google AutoFatory a while ago and ending up with the same pb so i guess this is a kapt problem?
Thx for help!
I'm trying to figure out how to support views with @InflationInject
annotated constructors whose dependencies are provided by subcomponents or components that only have a component dependency on the component that provides the InflationInjectFactory
. So basically I need to add view factories to the InflationInjectFactory
on the fly but it doesn't seem possible when I also have a single activity app. Is there something I'm missing?
Request not to mandate same names for assisted parameters. In my case, changing params to workerParameters fixed it but would be nice not to force same names.
InternalWorkerFactory.java:9: error: Factory method parameters do not match constructor @assisted parameters.
public abstract T create(@org.jetbrains.annotations.NotNull()
^
Missing:
The androidx.fragment
library recently added support for supplying a custom FragmentFactory
to handle creation (and re-creation) of Fragments. This opens a potential possibility for using Dagger to handle the creation and injection of your Fragments -- potentially supplying the arguments as @Assisted
similarly to how @InflationInject
is working.
Is this something you would consider adding to the library or do you think it would be better as a separate library?
This matches Dagger terminology.
A dependency request has:
AssistedInject searches classpath for @Generated
type https://github.com/square/AssistedInject/blob/cf2b4cf76f5769e880966bf215c00eb66c7c9259/assisted-inject-processor/src/main/java/com/squareup/inject/assisted/processor/internal/generatedAnnotation.kt#L29 but it does not take source version into account. This causes issues when both annotations are present in the classpath (e.g. running with JDK11 and having javax.annotation:javax.annotation-api:1.3.2
in the compile classpath).
Doing the same check as auto-common seems like a good solution: https://github.com/google/auto/blob/79c9d15c14a898e1ae2085f265a5396b190d7017/common/src/main/java/com/google/auto/common/GeneratedAnnotations.java#L54
I see the next error message:
symbol: class AssistedInject_AppAssistedInjectModule/app/build/tmp/kapt3/stubs/debug/com/android/AppComponent.java:40: error: [Dagger/MissingBinding] com.android.budgets.ui.overview.BudgetsOverviewViewModel.Factory cannot be provided without an @Provides-annotated method.
public abstract interface AppComponent {
^
com.android.budgets.ui.overview.BudgetsOverviewViewModel.Factory is injected at
com.android.budgets.ui.overview.BudgetsOverviewNavigation(viewModelFactory)
com.android.budgets.ui.overview.BudgetsOverviewNavigation is provided at
com.android.budgets.BudgetsComponent.overviewNavigation() [com.android.AppComponent โ com.android.user.UserSessionComponent โ com.android.user.UserComponent โ com.android.budgets.BudgetsComponent]/app/src/main/java/com/yolt/android/recovery/recover/ui/pin/PinDigitsViewModel.kt:12: warning: Assisted injection without at least one non-@Assisted parameter doesn't need a factory
class PinDigitsViewModel @AssistedInject constructor(@Assisted private val onComplete: (ByteArray) -> Unit) {
^app/build/tmp/kapt3/stubs/debug/com/android/dagger/AppAssistedInjectModule.java:9: error: [ComponentProcessor:MiscError] dagger.internal.codegen.ComponentProcessor was unable to process this interface because not all of its dependencies could be resolved. Check for compilation errors or a circular dependency with generated code.
If check the Model class I see it requires injection another class that has generics and one of the constructor params is also assisted injected class.
I saw an issue with generics and assisted inject. Or I wonder what another thing can be wrong. For example, this generic class operates with data binding generated code.
Found:
[android.content.Context context, android.util.AttributeSet attributeSet]
Expected:
[android.content.Context context, android.util.AttributeSet attrs]
Is it a bug?
After seeing Jake's presentation on this library at DroidCon, I was hoping that the proposed solution will solve the problem of passing dynamic parameters to Android Architecture Components ViewModel that is discussed on several places over the internet, originally probably here android/architecture-components-samples#207
Since ViewModel shouldn't be injected directly to a client as Presenter is in the examples of this library and Jake's talk, all I could come up with was double factories, the assisted factory for creating ViewModelFactory and then then ViewModelFactory to have the system lazily create the ViewModel.
Can someone see a better solution or would it require a specific fork of this library to auto generate this extra ViewModelFactory?
Ideally I would see the same solution as in Jake's presentation. No manual creation of ViewModel and no manual parameter passing, only the dynamic params to Factory create method.
My example:
The ViewModelFactory and "ViewModelFactoryFactory"
class WordDetailViewModelFactory @AssistedInject constructor(
private val repository: WordRepository,
@Assisted private val argument: WordDetailScreenArg
) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return WordDetailViewModel(repository, argument) as T
}
@AssistedInject.Factory
interface Factory {
fun create(argument: WordDetailScreenArg): WordDetailViewModelFactory
}
}
The ViewModel
class WordDetailViewModel(
private val repository: WordRepository,
argument: WordDetailScreenArg
) : ViewModel() {
val wordDetail: LiveData<Word> = repository.word(argument.word)
fun onDeleteWordClicked() {
wordDetail.value?.let {
repository.deleteWord(it.word)
}
}
}
Injection
@Inject
lateinit var viewModelFactory: WordDetailViewModelFactory.Factory;
override fun onAttach(context: Context?) {
super.onAttach(context)
val arguments: WordDetailScreenArg = ...
Injection.appComponent.inject(this)
viewModel = ViewModelProviders.of(this, viewModelFactory.create(arguments))[WordDetailViewModel::class.java]
}
Edit: looks like @hansenji has a solution at https://github.com/hansenji/ViewModelInject
Code similar to this:
class MyClass @AssistedInject constructor(
foobar: Foobar,
@Assisted a: String,
@Assisted b: String
) {
@AssistedInject.Factory
interface Factory {
fun create(a: String, b: String): MyClass
}
}
Results in:
Duplicate @Assisted parameters declared. Forget a qualifier annotation?
Since @Assisted
args are supplied by the caller, it seems duplicates should be permitted. Am I missing something? Is this an annotation processing constraint?
I've solved the problem by adding qualifying annotations to both the MyClass
constructor declaration and the factory function.
Given the following module:
class Outer {
@AssistedModule
@Module(includes = AssistedInject_TestModule.class)
public abstract class TestModule {}
}
Compilation fails with:
error: @AssistedModule's @Module must include AssistedInject_TestModule
I am not certain if nested module not being allowed is intentional or not but it seems it should either be allowed or have a specific error as output.
I have a failing unit test and will open a PR with that once I have this issue link.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.