我可以在使用 dagger2 进行依赖注入时只注入 super class 吗?

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

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

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

Here's how members injection methods work:

  1. 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.
  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.

已讨论此问题 here and here, follow up these for updates. But it is unlikely to change soon, cause Dagger 2 is close to release

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

1) 扩展应用程序 class 以能够创建通用组件并保留对它的引用。

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,它从 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) 最后,你必须实际注入每个 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);
    }
}

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

更新:将@Activity范围对象注入片段

在某些时候,我需要使用 custom scopes 将对象绑定到 Activity 生命周期。我决定扩展这个 post,因为它可能对某些人有所帮助。

假设你有一个 @Module class ActivityModule 和一个 @Subcomponent 接口 ActivityComponent.

您需要修改 DaggerActivityActivities 扩展 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);
}

然后,可以像这样创建 class 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);

}

至于Activities,扩展FragmentDaggerFragments只有一个实现方法:

public abstract void injectFragment(ActivityComponent component);

您应该可以在任何地方重复使用 Fragments。请注意, ActivityDagger 中的方法 super.onCreated() 应该在组件实例化之后调用。否则,当重新创建 Activity 状态时,您将获得 NullPointerException,因为将调用 Fragment 的方法 super.onCreate()

您可以使用反射进行一些修改:

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

在这种情况下,您仍然需要在组件中编写注入方法,但是您不需要在每个activity、片段、视图等中编写'inject'方法。

为什么有效?当我们在注入对象上使用 getClass() 时,将得到一个后代 class,而不是 base.

注意!如果你使用 Proguard,你需要添加 next -keep class <ComponentClass> { *; } 遵守您的规则,以便保持组件中的注入方法