是否可以避免在父 class 中进行不必要的注入?

Is it possible to avoid unnecessary injection in parent class?

给定以下示例:

class CustomView extends View {
    @Inject
    SomeObject mObject;

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        getApplicationComponent().inject(this);
    }
}

class SecondaryCustomView extends CustomView {
    @Inject
    AnotherObject mAnotherObject;

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        getApplicationComponent().inject(this);
    }
}

两个自定义视图都可以在布局上独立使用。第二个只是比第一个更专业一点。

如你所见,两者都有要注入的字段,都需要调用inject()。问题在于,当 SecondaryCustomView 调用它的 inject() 时,Dagger 注入了一个 AnotherObject 实例和一个 SomeObject 实例。在调用 super.onFinishInflate() 之后,它创建了 SomeObject 的第二个实例。这本身不是问题,但我们至少创建了不必要的对象。

有没有办法避免这种情况?告诉 Dagger 一个子 class 已经被注入的方法,所以忽略父注入?

例如,组件如下所示:

@Component(...)
public interface AppComponent {
    void inject(CustomView);
    void inject(SecondaryCustomView);
}

在 Dagger 中没有办法做到这一点,但你可以自己做。

在评论中同意并扩展您的观点:

yes it is needed. Dagger does not inject the child objects if we use injection on parent only. however it injects the parent objects if it is called from the child.

这是正确的,并在 "A note about covariance" 中指出:虽然 inject(Foo) 可以接受 Foo 或其任何子实例class,但 Dagger 是一个 compile-时间框架;不会生成 inject(Foo) 来注入属于 Foo 的任意子 class 的字段,因为在编译时这是不可能知道的。这可能有点令人惊讶,特别是如果您的组件同时具有 inject(Foo)inject(FooSubclass),就像您在此处为 CustomView 和 SecondaryCustomView 所具有的那样:使用名称 injectCustomViewinjectSecondaryCustomView 很明显只有前者可以从 Foo 中调用。

除了简单地将 injectedAlready 布尔字段设置为标志外,一种技术是创建一个可覆盖的方法,该方法不调用其 superclass 实现:

class CustomView extends View {
    @Inject
    SomeObject mObject;

    @Override
    protected void onFinishInflate() {
        injectMe();
        super.onFinishInflate();
    }

    protected void injectMe() {
        getApplicationComponent().inject(this); // inject(CustomView);
    }
}

class SecondaryCustomView extends CustomView {
    @Inject
    AnotherObject mAnotherObject;

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        // ...
    }

    /** Despite looking identical, the JVM can call the more-specific overload here. */
    @Override protected void injectMe() {
        getApplicationComponent().inject(this); // inject(SecondaryCustomView)
    }
}

如果您正在为 Activity 和 Fragment class 寻找类似的解决方案,您可以使用 dagger.android;那里的内置机制使用 class 的运行时类型从 Map 中动态获取正确的 AndroidInjector。但是,该解决方案目前不支持 View,因此对于您的具体情况,这是尽可能接近的。