具有BaseActivity的Android Dagger 2可减少样板 [英] Android Dagger 2 with BaseActivity to reduce boilerplate

查看:82
本文介绍了具有BaseActivity的Android Dagger 2可减少样板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在将每个活动中的一些Dagger 2样板代码移至BaseActivity时遇到了麻烦。

I'm having some troubles while I want to move some Dagger 2 boilerplate code in each activity to a BaseActivity.

BaseActivity extends AppCompatActivity

我有多项活动,例如:

ActivityA extends BaseActivity implements InterfaceA;
ActivityB extends BaseActivity implements InterfaceB;
...

在每个活动中,我都有类似的方法(其中X是A ,B,C,...针对每个活动):

In each activity I have a methods like this (where X is A, B, C, ... for each activity):

public void initActivity() {
   ComponentX compX;
   ...
   compX = appComponent.plus(new ModuleX(this)); // this == InterfaceX
   ...
   compX.inject(this); // this == ActivityX
}

我试图减少此代码,将其移动到父BaseActivity。但是我遇到了一些问题。我认为也许可以使用泛型来实现,但我不知道具体如何。

I was trying to reduce this code, moving it to the parent BaseActivity. But I'm having some problems to do it. I think that maybe with generics I could do it, but I don't know exactly how.

推荐答案

这是问题:您正在调用 inject 方法,并且Java是否可以在编译时确定?

Here's the question: Which inject method are you calling, and can Java determine that at compile time?

关于协方差的注释 ,Dagger将生成您定义的任何成员注入方法的代码,但您传入的静态类型。

As described in "A note about covariance", Dagger will generate code for any members-injection method you define, but only the static type you pass in.

@Component public interface YourComponent {
  void injectBase(BaseActivity baseActivity);
  void injectA(ActivityA activityA);
  void injectB(ActivityB activityB);
  void injectC(ActivityC activityC);
}

调用 injectA 时并传递一个ActivityA实例,您将获得对ActivityA中定义的字段(包括BaseActivity中的字段)的注入。与ActivityB和ActivityC相同。但是,如果调用 injectBase ,即使传入的对象恰好是ActivityA,ActivityB或ActivityC,Dagger也只会注入属于BaseActivity的字段。 Dagger会在编译时生成代码,因此,如果您调用 injectBase ,则注入只会针对BaseActivity上的字段进行,因为这是为BaseActivity的成员注入器生成的代码,并且这些是Dagger唯一知道如何注入BaseActivity参数的字段。

When calling injectA and passing an ActivityA instance, you'll get injection for the fields defined in ActivityA including the fields in BaseActivity. Same with ActivityB and ActivityC. However, if you call injectBase, Dagger will only inject the fields belonging to BaseActivity, even if the object you pass in happens to be an ActivityA, ActivityB, or ActivityC. Dagger generates code at compile time, so if you call injectBase, the injection will only happen for the fields on BaseActivity—because that's the code that was generated for BaseActivity's members injector, and those are the only fields Dagger knows how to inject for a BaseActivity parameter.

自然地,因为BaseActivity只知道 this 是BaseActivity的子类型,它只能调用 injectBase ,而不能调用任何特定的子类型。重要的是,即使所有名称 injectBase injectA 等所有都是相同(例如注入)。 JVM将选择在编译时可以确定的最窄的重载,即 inject(BaseActivity),这将注入BaseActivity的字段,而在子类型中则不进行任何操作。如果您要对它们进行独特的命名,则会看到您正在呼叫的对象,以及为什么它没有注入子类型字段。

Naturally, because BaseActivity only knows that this is a subtype of BaseActivity, it can only call injectBase and not any specific subtypes. Importantly, this remains true even if all the names injectBase, injectA, and so forth, are all the same (like inject). The JVM will pick the narrowest overload it can determine at compile time, which will be inject(BaseActivity), which will inject BaseActivity's fields and nothing in subtypes. If you were to name them uniquely, you'd see which one you're calling, and why it's not injecting subtype fields.

泛型在这里无济于事:您正在寻找您的组件来为ActivityA,ActivityB和ActivityC生成并调用成员注入器。泛型将被删除,但是该组件不能采用BaseActivity的任意子类:Dagger无法在编译时为其仅在运行时可能遇到的类型生成代码。您确实需要在编译时在Dagger中准备这些类型。

Generics won't help here: You're looking for your Component to generate and call members injectors for ActivityA, ActivityB, and ActivityC. Generics will be erased, but furthermore the component can't take an arbitrary subclass of BaseActivity: Dagger can't generate code at compile time for types it might only encounter at runtime. You really need to prepare those types in Dagger at compile time.

一种解决方法是允许子类型自行注入。子类型知道 this 是ActivityA(依此类推),即使代码看起来像字符一样,Java仍可以识别正确的类型并进行编译

One way around this is to allow the subtypes to inject themselves. The subtypes know that this is ActivityA (and so forth), and even though the code might look character-for-character the same, Java can identify the right type and compile it correctly.

// in BaseActivity
protected abstract void injectDependencies();

// in ActivityA
@Override protected void injectDependencies() { component.injectA(this); }

不过,还有一个最近发布的选项,使用 dagger.android ,它使用Multibindings和(有效) Map< Class,MembersInjector> 动态地注入所需的特定类型。这也适用于超类,以至于您可以让Activity扩展DaggerActivity,一切都将按照您想要的方式工作。 (请咨询 dagger.android.support 对应您的AppCompatActivity的软件包 DaggerAppCompatActivity

However, there's another recently-released option, using dagger.android, which uses Multibindings and (effectively) a Map<Class, MembersInjector> to dynamically inject the specific type you want. This works from a superclass, too, to the point that you can have your Activity extend DaggerActivity and everything will work just the way you'd like. (Consult the dagger.android.support package for your AppCompatActivity equivalent DaggerAppCompatActivity.)

这篇关于具有BaseActivity的Android Dagger 2可减少样板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆