java 使用 dagger2 进行依赖注入时,我可以只注入超类吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29312911/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 15:02:05  来源:igfitidea点击:

Can I just inject super class when use dagger2 for dependency injection?

javaandroiddependency-injectiondaggerdagger-2

提问by Chris.Zou

I use Dagger2 for DI in my android application. I found that I have to write inject method for every class that uses @Inject field. Is there a way that I can just inject the parent class so that I don't have to call inject on every subclass? Take Activity for example. I have a BaseActivitythat that every Activity extends from. Is there a way that I can just create an inject method in the component for BaseActivity and just call inject in BaseActivity's onCreate, and @inject fields in sub activities get injected automatically?

我在我的 android 应用程序中使用 Dagger2 进行 DI。我发现我必须为每个使用 @Inject 字段的类编写注入方法。有没有一种方法可以只注入父类,这样我就不必在每个子类上调用注入?以活动为例。我有一个BaseActivity每个 Activity 都延伸的那个。有没有一种方法可以让我在 BaseActivity 的组件中创建一个注入方法,然后在 BaseActivity 的 onCreate 中调用注入,然后自动注入子活动中的 @inject 字段?

采纳答案by Kirill Boyarshinov

It cannot be done right now. Explanation by Gregory Kick (from here):

现在无法完成。Gregory Kick 的解释(来自这里):

Here's how members injection methods work:

  1. You can make a members injection method for any type that has @Injectanywhere in its class hierarchy. If it doesn't, you'll get an error.
  2. All @Injected members in the entire type hierarchy will be injected: the argument type and all supertypes.
  3. No members will be @Injected for subtypes of the argument type.

以下是成员注入方法的工作原理:

  1. 您可以为@Inject在其类层次结构中的任何位置的任何类型创建成员注入方法。如果没有,您将收到错误消息。
  2. @Inject将注入整个类型层次结构中的所有ed 成员:参数类型和所有超类型。
  3. 不会@Inject为参数类型的子类型编辑任何成员。

This issue was discussed hereand here, follow up these for updates. But it is unlikely to change soon, cause Dagger 2 is close to release.

这个问题在这里这里讨论过,跟进这些更新。但不太可能很快改变,因为 Dagger 2即将发布

回答by Gordak

I encountered the same situation. One way to ease a bit the injection from a common component in all Activities is the following:

我遇到了同样的情况。从所有活动中的公共组件中稍微简化注入的一种方法如下:

1) Extend the Application class to be able to create the common component and keep a reference to it.

1) 扩展 Application 类以能够创建公共组件并保留对它的引用。

public class ApplicationDagger extends Application {

    private ApplicationComponent component;

    @Override
    public void onCreate(){
        super.onCreate();
        component = DaggerApplicationComponent.builder().applicationModule(new ApplicationModule(this)).build();
    }

    public ApplicationComponent getComponent(){
            return component;
    }
}

2) Create an abstract DaggerActivity which gets the common component from Application and calls an abstract method injectActivity, giving the component as an argument. Like this:

2) 创建一个抽象的 DaggerActivity,它从 Application 获取公共组件并调用抽象方法injectActivity,将组件作为参数。像这样:

public abstract class DaggerActivity extends Activity {

    @Override
    public void onCreate(Bundle saved){
        super.onCreate(saved);
        ApplicationComponent component = ((ApplicationDagger) getApplication()).getComponent();
        injectActivity(component);
    }

    public abstract void injectActivity(ApplicationComponent component);
}

3) Last, you have to actually inject each Activityextending DaggerActivity. But this can be done with less efforts now, as you have to implement the abstractmethod otherwise you'll get compile errors. Here we go:

3)最后,您必须实际注入每个Activity扩展的DaggerActivity. 但这现在可以用更少的努力来完成,因为您必须实现该abstract方法,否则您会遇到编译错误。开始了:

public class FirstActivity extends DaggerActivity {

    @Inject
    ClassToInject object;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //initialize your Activity
    }

    @Override
    public void injectActivity(ApplicationComponent component) {
        component.inject(this);
    }
}

Of course, you still have to declare each Activity explicitly in your Component.

当然,您仍然需要在您的组件中显式声明每个 Activity。

UPDATE : Injecting @ActivityScope objects into Fragments

更新:将@ActivityScope 对象注入 Fragments

At some point, I needed to use custom scopesto bind objects to an Activitylife cycle. I decided to extends this post as it might help some people.

在某些时候,我需要使用自定义范围将对象绑定到 Activity生命周期。我决定扩展这篇文章,因为它可能会帮助一些人。

Let's say you have a @Moduleclass ActivityModuleand a @Subcomponentinterface ActivityComponent.

假设您有一个@ModuleActivityModule和一个@Subcomponentinterface ActivityComponent

You would need to modify the DaggerActivity. The Activitiesextending DaggerActivitywould need to implement the new method (change of signature).

您需要修改DaggerActivity.该Activities扩展DaggerActivity需要实现新的方法(签字的变化)。

public abstract class ActivityDagger extends AppCompatActivity {

    ActivityComponent component;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        component = ((ApplicationDagger) getApplication()).getComponent().plus(new ActivityModule(this));
        injectActivity(component);
        super.onCreate(savedInstanceState);
    }

    ActivityComponent getComponent() {
        return component;
    }

    public abstract void injectActivity(ActivityComponent component);
}

Then, a class FragmentDaggerextending Fragmentcan be created like this :

然后,可以像这样创建一个FragmentDagger扩展Fragment类:

public abstract class FragmentDagger extends Fragment {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityDagger activityDagger = (ActivityDagger) getActivity();
        ActivityComponent component = activityDagger.getComponent();
        injectFragment(component);
    }

    public abstract void injectFragment(ActivityComponent component);

}

As for the Activities, the Fragmentsextending FragmentDaggerhave only one method to implement:

至于ActivitiesFragments扩展FragmentDagger只有一种方法可以实现:

public abstract void injectFragment(ActivityComponent component);

You should be able to reuse Fragmentswherever you want. Notice that the method super.onCreated()in ActivityDaggershould be called after the component instantiation. Otherwise, you will get NullPointerExceptionwhen the Activitystate is recreated, because the method super.onCreate()of the Fragmentwill be called.

你应该能够在Fragments任何你想要的地方重复使用。请注意,应该在组件实例化之后调用super.onCreated()in方法ActivityDagger。否则,你会得到NullPointerException异常时的Activity状态重新创建,因为该方法super.onCreate()Fragment将被调用。

回答by Andriy Pokynboroda

You can do a little hack using reflection:

您可以使用反射做一些小技巧:

public class UiInjector {

    private static final String METHOD_NAME = "inject";

    private final UIComponent component;

    public UiInjector(final UIComponent component) {
        this.component = component;
    }

    public void inject(final Object subject) {
        try {
            component.getClass()
                    .getMethod(METHOD_NAME, subject.getClass())
                    .invoke(component, subject);
        } catch (final NoSuchMethodException exception) {
            throwNoInjectMethodForType(component, subject.getClass());
        } catch (final Exception exception) {
            throwUnknownInjectionError(exception);
        }
    }

    private void throwNoInjectMethodForType(final Object component, final Class subjectType) {
        throw new RuntimeException(component.getClass().getSimpleName() +
                " doesn't have inject method with parameter type : " + subjectType);
    }

    private void throwUnknownInjectionError(final Exception cause) {
        throw new RuntimeException("Unknown injection error", cause);
    }
}

In this case, you still need to write inject method in a component, but you don't need 'inject' method in each activity, fragment, view, whatever.

在这种情况下,您仍然需要在组件中编写注入方法,但不需要在每个活动、片段、视图等中都使用“注入”方法。

Why it's work? when we use getClass()on injection subject will get a descendant class, not base.

为什么是工作?当我们getClass()在注入主题上使用时,会得到一个后代类,而不是基类。

Caution! In case you use Proguard, you need to add next -keep class <ComponentClass> { *; }to your rules in order to keep inject methods as is in component

警告!如果您使用 Proguard,则需要-keep class <ComponentClass> { *; }在规则旁边添加 ,以便在组件中保持注入方法的原样