在使用dagger2进行依赖注入时,我能否只注入超类?

51
我在我的Android应用程序中使用Dagger2进行依赖注入。我发现我必须为每个使用@Inject字段的类编写inject方法。有没有一种方法可以只注入父类,这样我就不必在每个子类上调用inject了呢?
以Activity为例。我有一个BaseActivity,每个Activity都是从它继承而来的。有没有一种方法可以只在组件中为BaseActivity创建一个inject方法,在BaseActivity的onCreate方法中调用inject,然后子活动中的@Inject字段自动被注入?

你能否添加一些示例代码来展示你的意思? - nhaarman
只是一个问题(如果我正确理解了你的问题),为什么不在基本活动中定义注入字段并在子活动中使用它。任何这样的情况,您希望您的“子活动中的注入字段自动注入”。 - JSONParser
3个回答

43

我遇到了相同的情况。缓解在所有活动中从共同组件中注入的压力的一种方法如下:

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)创建一个抽象的DaggerActivity,它从应用程序中获取公共组件,并调用一个抽象方法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) 最后,您需要实际注入每个扩展DaggerActivityActivity。 但是现在可以用更少的工作完成这项工作,因为您必须实现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);
    }
}

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

更新:将@ActivityScope对象注入到Fragment中

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

假设你有一个@ModuleActivityModule和一个@Subcomponent接口ActivityComponent

你需要修改DaggerActivity。继承DaggerActivityActivities需要实现新的方法(更改签名)。

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);
}

那么,可以像这样创建一个扩展Fragment的类FragmentDagger

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);

}

关于 Activities,扩展自FragmentDaggerFragments只需要实现一个方法:

public abstract void injectFragment(ActivityComponent component);

你应该可以在任何地方重复使用Fragments。请注意,在创建组件后,应在ActivityDagger 中调用 super.onCreated() 方法。否则,在重新创建Activity状态时,会出现空指针异常,因为将调用Fragmentsuper.onCreate()方法。


34

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

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

  1. 您可以为任何具有类层次结构中任何位置的@Inject的类型创建成员注入方法。如果没有,则会出现错误。
  2. 将注入整个类型层次结构中的所有@Inject成员:参数类型和所有超类型。
  3. 不会为参数类型的子类型注入任何成员@Inject

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


似乎他们有理由做出这个决定。但是我认为很遗憾他们不支持这一点,因为这有点违反直觉。不过还是谢谢你的回答! - Chris.Zou
@Chris.Zou 为了支持子类注入,您需要在运行时进行反射。Dagger 2团队早期决定避免在运行时执行操作,因为它会变慢,并且只有在运行应用程序时才能发现错误。 - vaughandroid

1
您可以使用反射进行一些小的黑客操作:

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);
    }
}

在这种情况下,您仍然需要在组件中编写inject方法,但是您不需要在每个活动、片段、视图等中使用'inject'方法。
为什么会起作用?当我们在注入主题上使用getClass()时,将获得一个后代类,而不是基类。
注意!如果您使用Proguard,则需要添加以下内容-keep class <ComponentClass> { *; }到您的规则中,以保留组件中的注入方法。

1
Dagger应该在编译时进行检查,因此这样做会破坏这个目的... - AA_PV

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接