Comments (24)
Here's how members injection methods work:
- You can make a members injection method for any type that has
@Inject
anywhere in its class hierarchy. If it doesn't, you'll get an error. - All
@Inject
ed members in the entire type hierarchy will be injected: the argument type and all supertypes - No members will be
@Inject
ed for subtypes of the argument type.
So, given the following:
final class Dep {
@Inject Dep() {}
}
class A {
@Inject Dep a;
}
class B extends A {
@Inject Dep b;
}
class C extends B {
@Inject Dep c;
}
@Component
interface TestComponent {
B injectB(B instance);
}
The following test will pass:
TestComponent component = Dagger_TestComponent.create();
C instance = component.injectB(new C());
assertThat(instance.a).isNotNull();
assertThat(instance.b).isNotNull();
assertThat(instance.c).isNull();
from dagger.
I should clarify that component
has methods for both the subclass and base class.
public interface MyComponent {
void inject(BaseActivity activity);
void inject(MainActivity activity);
}
from dagger.
So what's the question or what doesn't work?
On Nov 16, 2014 1:22 PM, "Prateek Srivastava" [email protected]
wrote:
I should clarify that component has methods for both subclass and base
class.public interface MyComponent {
void inject(BaseActivity activity);void inject(MainActivity activity);
}—
Reply to this email directly or view it on GitHub
#73 (comment).
from dagger.
Yeah accidentally hit enter too soon, but is there a pattern for injecting dependencies from a base class, rather than calling inject in each of the subclasses manually.
from dagger.
In Dagger 1, you could call
inject
from a base class and have all dependencies satisfied for subclasses.
This is true iff you included all subclasses on a module's injects=
which was included in the graph with which you were injecting.
Dagger 2 is no different. You need to include all subclasses in a component so that the generated code can be wired to fulfill the contracts.
from dagger.
so this means in Dagger 2 we'll have to explicitly call inject
in each of my Activity classes like the following:
@Singleton
@Component
interface AppComponent {
BaseActivity inject(BaseActivity activity);
SomeActivity inject(SomeActivity activity);
}
class BaseActivity extends Activity {
[snip]
void onCreate() {
super.onCreate(savedInstanceState);
appComponent = Dagger_AppComponent.builder()
[snip]
.build();
}
}
AppComponent getComponent() {
return appComponent;
}
}
class SomeActivity extends BaseActivity {
@Inject Dep dep;
void onCreate() {
super.onCreate(savedInstanceState);
getComponent().inject(this);
}
}
In Dagger 1 it's only needed to call inject
in BaseActivity
and the injection to subclass instances are all handled. I can live with Dagger 2's more explicit injection but it would be a bit simpler to convert existing projects with Dagger 1 way.
from dagger.
@rlei No, it doesn't. It can still be in the base class.
from dagger.
@JakeWharton no it doesn't work, as far as I tested.
Basically what I did is in my last comment. Putting getComponent().inject(this)
in BaseActivity.onCreate()
only injects for BaseActivity
. I had to add getComponent().inject(this)
to each subclass onCreate()
individually to let the injection happen.
My module and component code is as following:
@Module
public class AppModule {
[snip]
public AppModule(Application application) {
this.application = application;
}
@Provides @Singleton
DaoSession provideDaoSession() {
[snip]
}
}
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
void inject(MyApplication application);
void inject(BaseActivity activity);
void inject(ExpenseDetailsActivity activity);
void inject(MainActivity activity);
[snip]
}
Did I miss anything?
[EDIT]: also updated my last comment.
from dagger.
Ah, right right. This is in the JLS.
@gk5885 The difference in your example is that it would be a method in A
calling inject(this)
rather than one knowing about C
.
Here's a fully working example of the problem:
abstract class BaseThing {
@Inject String foo;
void doIt() {
Dagger_BaseComponent.create().inject(this);
}
}
class RealThing extends BaseThing {
@Inject CharSequence bar;
}
@Module
class BaseModule {
@Provides String provideString() { return "String!"; }
@Provides CharSequence provideCharSequence() { return "CharSeq!"; }
}
@Component(modules = BaseModule.class)
interface BaseComponent {
void inject(BaseThing baseThing);
void inject(RealThing realThing);
}
public class BaseExample {
public static void main(String... args) {
RealThing realThing = new RealThing();
realThing.doIt();
System.out.println("String? => " + realThing.foo);
System.out.println("CharSeq? => " + realThing.bar);
}
}
This was a common pattern in Dagger 1.
from dagger.
Right, this will definitely be a change from how things were done in D1. Essentially, the ObjectGraph
used to do a bunch of reflection to figure out the type of the object being injected and dispatch to the right logic. You could do exactly the same thing if you wanted, but the cost of the little extra bit of verbosity makes for a much more efficient code path.
Plus, in experimenting with Android apps in D2 we found that infrastructure around managing components (as opposed to ObjectGraph
s) really wasn't worth its weight. We had apps with a base activity that held a reference to a graph, but when we moved it to D2 it just really didn't have as much value since the components are strongly typed and often more granular (i.e. you may end up with separate activity-scoped components per activity rather than a huge graph for all activities). Take a look at https://github.com/gk5885/dagger-android-sample/ and see if the pattern in that (albeit trivial) example works for you.
from dagger.
@gk5885 That sounds good to me! It should be something that should be mentioned in the migration guide when it is released.
from dagger.
@f2prateek I made an abstract method in my BaseActivity that creates the Component
and calls inject
. That way each concrete class only have to do something like Dagger_FooComponent.create().inject(this)
.
from dagger.
What is the use case here for library projects? I have a library project with methods accepting and abstract class containing a field the user will need to do some work. That field has to be injected from the library at runtime. The programmer can't go and add those lines of code to the component interface.
from dagger.
@pakoito, I'm not quite following what you're asking. Can you show some example code?
from dagger.
In a library module:
public abstract class BaseSystem {
@Inject
protected World projectWorld;
}
@Singleton
public class World {
@Inject
public World(){}
public void hey(){}
}
public class Framework {
@Component
public interface MyComponent {
void inject(BaseSystem system);
}
public Framework(BaseSystem mySystem) {
// Ideally, but not current
component.inject(mySystem);
}
}
And then in my impl module:
public abstract class ImplSystem extends BaseSystem {
public void doWork(){
projectWorld.hey();
}
At the impl module we have no access to MyComponent to add new classes to be injected.
from dagger.
My current implementation uses Artemis-odb for this injections, but they use Reflection so it's a tradeoff Dagger may not want to make.
from dagger.
Performing the dependency injection in an abstract base class of Activity
, Fragment
, etc. seems to be a common pattern in Android development with Dagger 1 because it's very handy. I'm using it, too.
How this can be achieved with Dagger 2 respectively alternative patterns should be clearly documented in a (migration) guide.
from dagger.
Maybe injecting dependencies in a base class would work if a component supports generics? See #65.
from dagger.
FYI, I've just pushed out a 1.0-SNAPSHOT of Bullet • to the Sonatype OSS repository, so you can make @JakeWharton's example above work (well, it should work, I haven't tested):
abstract class BaseThing {
@Inject String foo;
void doIt() {
// Wrap the Component into the generated ObjectGraph:
new Bullet_BaseComponent(Dagger_BaseComponent.create()).inject(this);
}
}
class RealThing extends BaseThing {
@Inject CharSequence bar;
}
@Module
class BaseModule {
@Provides String provideString() { return "String!"; }
@Provides CharSequence provideCharSequence() { return "CharSeq!"; }
}
@Component(modules = BaseModule.class)
interface BaseComponent {
// You don't need to include a method for BaseThing here, only your concrete subclasses.
void inject(RealThing realThing);
}
public class BaseExample {
public static void main(String... args) {
RealThing realThing = new RealThing();
realThing.doIt();
System.out.println("String? => " + realThing.foo);
System.out.println("CharSeq? => " + realThing.bar);
}
}
from dagger.
This is less an issue, and more a discussion - I'm not sure where to go from here, but I'm going to close the issue for now, and if there's a feature request and/or further relevant discussion, it can be reopened. Given that #102 seems to be related, maybe further discussion can happen there.
from dagger.
@JakeWharton your example does not work for me.
The DaggerBaseComponent.create().inject(this)
call in BaseThing
uses the inject(BaseThing baseThing)
method and not the inject(RealThing realThing)
so realThing.bar
is null
.
I fixed it with a doIt()
method in RealThing
.
But I have another question.
How does the code look like if RealThing
want's to add it's own Component / Modul? If I create a RealComponent / Modul the BaseThing
injection doesn't work. Do I have to add all subclass injections in the BaseModul?
from dagger.
Tried the example posted by @JakeWharton, and member in SubClass was not injected. However, as I explore more in the generated code, that could possibly be done via the following:
abstract class BaseThing {
@Inject
String foo;
public BaseThing() {
this(DaggerBaseComponent.create());
}
public BaseThing(BaseComponent component) {
component.inject(this);
}
}
class RealThing extends BaseThing {
@Inject CharSequence bar;
public RealThing() {
this(DaggerBaseComponent.create());
}
public RealThing(BaseComponent component) {
component.inject(this);
}
}
@Module
class BaseModule {
@Provides
String provideString() { return "String!"; }
@Provides CharSequence provideCharSequence() { return "CharSeq!"; }
}
@Component(modules = BaseModule.class)
interface BaseComponent {
void inject(BaseThing baseThing);
void inject(RealThing realThing);
}
public class BaseExample {
public static void main(String... args) {
RealThing realThing = new RealThing();
System.out.println("String? => " + realThing.foo);
System.out.println("CharSeq? => " + realThing.bar);
}
}
This way, bar is injected properly.
from dagger.
@Singleton
@Component(modules = {ABC.class})
public interface BaseComponent<T extends Base> {
void inject(T handler);
}
I tried to do something like this but compiled error. Any approach for this? Thanks
from dagger.
@vinhnemo Dagger's job is to generate an implementation of your interface, but Dagger has no idea what T
is so there's no way for Dagger to know how to "inject" T
.
You probably want to start a new issue since this seems like a different question, and please add more details about what you're actually trying to accomplish.
from dagger.
Related Issues (20)
- Annotate interface with default Bind implementation HOT 2
- Transitive vulnerability from symbol-processing-api HOT 1
- java.lang.IllegalArgumentException: CreationExtras must have a value by `SAVED_STATE_REGISTRY_OWNER_KEY` HOT 4
- Update of different dependency: Error "package javax.annotation.processing is not visible" HOT 4
- Compile time error with KSP with Generics HOT 4
- java.lang.IllegalArgumentException: Multiple entries with same key HOT 4
- Execution failed for task ':app:kaptDebugKotlin'. > A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask$KaptExecutionWorkAction > java.lang.reflect.InvocationTargetException (no error message) HOT 2
- Stackoverflow when exposing parents of Subcomponents HOT 7
- Hilt not working with android testFixtures? HOT 2
- Hilt 2.44.2 - Class not found when use @AndroidEntryPoint at WidgetReceiver HOT 1
- Does Dagger 2.51 support JDK17 and JDK21? HOT 1
- [KSP] Intersection type bounds are not ordered correctly in generated factories HOT 1
- [KSP] KSP mode does not check for `@Provides` on interface methods HOT 1
- [KSP] Multibinding example from developer docs does not compile under Kotlin + KSP HOT 1
- [KSP] How to pass compiler argument to KSP? HOT 4
- The lack of sprite in the Friday Night Funkin character
- Subcomponent MembersInjector error in 2.51 HOT 1
- Does Hilt always execute app module during instrumented test? HOT 3
- [KSP] Dagger-KSP does not see typealiased dagger annotations HOT 3
- Cannot invoke getAnnotationValue because "annotation" is null HOT 14
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from dagger.