50

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

4

3 回答 3

42

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

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 ,它从 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);
    }
}

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

更新:将@ActivityScope 对象注入片段

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

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

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

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

}

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

public abstract void injectFragment(ActivityComponent component);

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

于 2015-06-25T13:41:21.140 回答
34

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

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

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

此问题已在此处此处进行了讨论,请跟进这些以获取更新。但它不太可能很快改变,因为 Dagger 2即将发布

于 2015-03-29T05:40:43.703 回答
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);
    }
}

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

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

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

于 2017-06-06T15:12:35.280 回答