Android和Dagger 2.10 AndroidInjector

Android上常见的Dagger设置通常涉及Application Component和Application Module,其中前者用于注入组件(例如Activity,Fragment等)。

@Component(modules = { AppModule.class })
public interface AppComponent {
    @Component.Builder
    interface Builder {
        @BindsInstance Builder application(App application);
        AppComponent build();
    }    
    void inject(FeatureActivity featureActivity);
}
@Module
public class AppModule {
    @Provides Context provideContext(App application) {
        return application.getApplicationContext();
    }
    @Singleton @Provides SomeClientApi provideSomeClientApi() {
        return new SomeClientApiImpl();
    }
}
public class App extends Application {
    private AppComponent appComponent;
    @Override
    public void onCreate() {
        super.onCreate();
        appComponent = DaggerAppComponent
            .builder()
            .application(this)
            .build();
    }
    public AppComponent getAppComponent() {
        return appComponent;
    }
}

由于Android框架为我们实例化了这些组件,所以我们必须执行成员注入,使用@Inject 注释包可见的类字段,如下所示:

public class FeatureActivity extends AppCompatActivity {
  @Inject SomeClientApi mSomeClientApi; 
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ((App)getApplication())
        .getAppComponent()
        .inject(this);
  }
}

然而,这种模式打破了依赖注入的核心原则:一个类不应该知道它是如何注入的。新推出的dagger-android模块专门解决了这个问题 - 将注入的类与注入器分离。使用新的dagger-android模块,首先,将以下Gradle依赖项添加到build.gradle中:

// Dagger core dependencies
annotationProcessor 'com.google.dagger:dagger-compiler:2.10'
compile 'com.google.dagger:dagger:2.10'
// Dagger Android dependencies
annotationProcessor 'com.google.dagger:dagger-android-processor:2.10'
compile 'com.google.dagger:dagger-android:2.10'
// Use this instead if you're using support library
compile 'com.google.dagger:dagger-android-support:2.10'

如果您从旧版本的Dagger进行升级,请确保将版本(2.10)设置为所有与匕首相关的依赖关系。另外值得一提的是,我在本教程中使用Android Gradle插件2.3.0。如果您使用的版本比2.2.0老,你要替换annotationProcessor用apt。在我们的项目中,新模块的设置不仅仅需要一个组件和一个模块。我的建议是具有以下类结构:

/
| App (extending Application)
| AppComponent
| AppModule
| BuildersModule

  • feature/
    | FeatureModule
    | FeatureSubComponent

我们可以看到,在上面的典型设置之上,我们还添加了3个类(BuildersModule,FeatureModule,FeatureSubComponent)。每个功能都有自己的sub component和module。

我使用术语“ Feature”来描述应用程序中的一个屏幕(或Activity)。

1.subcomponent必须继承AndroidInjector<TheActivity>并具有继承AndroidInjector.Builder<TheActivity>的抽象类

@Subcomponent
public interface FeatureSubComponent extends AndroidInjector<FeatureActivity> {
    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<FeatureActivity> {
    }
}

2.然后,我们需要在BuildersModule中添加绑定到sub component的Builder中,以便Dagger能够注入FeatureActivity。后期的 sub components builders的绑定也需要添加到此类中。

@Module
public abstract class BuildersModule {
    @Binds
    @IntoMap
    @ActivityKey(FeatureActivity.class)
    abstract AndroidInjector.Factory<? extends Activity> bindFeatureActivityInjectorFactory(FeatureActivitySubComponent.Builder builder);
// Add more bindings here for other sub components
}

在此为其他子组件添加更多绑定

3.接下来,我们需要连接FeatureSubComponent,AppModule通过在@Module注解里面指定subcomponents

@Module(subcomponents = { FeatureSubComponent.class })
public class AppModule {
    @Provides Context provideContext(App application) {
        return application.getApplicationContext();
    }
    @Singleton @Provides SomeClientApi provideSomeClientApi() {
        return new SomeClientApiImpl();
    }
}

4.和电线
BuildersModule到AppComponent:

@Component(modules = {
        /* Use AndroidInjectionModule.class if you're not using support library */
        AndroidSupportInjectionModule.class,
        AppModule.class,
        BuildersModule.class })
public interface AppComponent {
    @Component.Builder
    interface Builder {
        @BindsInstance Builder application(App application);
        AppComponent build();
    }
    void inject(App app);
}

请注意AppComponent:除了添加BuildersModule的@Component(modules= {…}),我们还增加了AndroidSupportInjectionModule,这是一个内置的模块匕首,机器人必须安装在AppComponent按照该规定的官方文档,这是必要的,以确保所有绑定必要对这些基站类型可用。
我们删除了inject()方法,FeatureActivity并用inject()我们的App类的方法替换了它。

5.然后,我们修改我们的App类,以便它实现HasDispatchingActivityInjector和@Inject一个DispatchingAndroidInjector<Activity>从返回activityInjector()的方法。在onCreate(),我们建立AppComponent并且调用inject(this)。

public class App extends Application implements HasDispatchingActivityInjector {
    @Inject DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        DaggerAppComponent
                .builder()
                .application(this)
                .build()
                .inject(this);
    }

    @Override
    public DispatchingAndroidInjector<Activity> activityInjector() {
        return dispatchingAndroidInjector;
    }
}

请注意,在Dagger 2.11版中,所有HasDispatchingInjector的界面都已被重命名为HasInjectors
最后,FeatureActivity我们删除了我们在那里注入活动的代码,而我们在调用之前调用,因为调用超级在配置更改期间附加上一个活动实例的Fragments,而后者又会注入Fragments。为了使Fragment注入成功,必须注意Activity:AndroidInjection.inject(this) super.onCreate()

public class FeatureActivity extends AppCompatActivity implements FeatureView {
   @Inject SomeClientApi mSomeClientApi;
   @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        AndroidInjection.inject(this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

向Activity自己的组件注入自定义参数
在某些情况下,您需要注入活动本身提供的参数。之前完成的常见方法是在调用inject()活动的组件之前将参数传递给模块的构造函数。例如,如果我们按照这里描述的MVP设计模式,我们的Presenter将把View作为构造函数参数的一部分。这意味着我们需要将该活动作为参数传递给模块构造函数。在匕首android之前,这样做是这样做的:

@Module
class FeatureModule {
    private FeatureView view;

    public FeatureModule(FeatureView view) {
        this.view = view;
    }

    @Provides FeatureView provideView() {
        return view;
    }
}

在演示者中,我们将使用构造函数注入:

class FeaturePresenter {
    private final FeatureView view;
    
    @Inject
    Presenter(FeatureView view) {
        this.view = view;    
    }
    public void doSomething() {
    }
}

最后,构建组件,传递一个新的模块实例并注入该活动:

public class FeatureActivity extends AppCompatActivity implements FeatureView {
    @Inject FeaturePresenter presenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerFeatureComponent.builder()
                .featureModule(FeatureModule(this)).build()
                .inject(this)
        
        // presenter ready to be used
        presenter.doNothing();
    }
}

但是我们如何用新的匕首 - Android模块呢?毕竟,我们在活动中只有一个调用 -  没有办法像以前那样传递一个新的模块实例。
AndroidInjection.inject(this)

答案是,我们不再需要这样做了。使用dagger-android模块,该活动已经是图表的一部分。那实际上是什么意思呢?这意味着我们所有的都创建一个绑定,将在任何FeatureView请求的地方注入活动。这样做的方法是使用Dagger的@Binds注释。为此,我们将创建一个新的模块类FeatureModule,它将包含所有特性绑定。

@Module
public abstract class FeatureModule {
    @Binds
    abstract FeatureView provideFeatureView(FeatureActivity featureActivity);
    }
}

这是伟大的,但你可能想知道,如果我想传递除活动之外的其他参数呢?假设您有一个唯一的ID,通过活动的附加功能传递给活动,演示者需要它。例如,假设演示者需要此ID才能发出HTTP请求。执行此操作的方法是使用带有@Named注释的限定符。所以我们的主持人会像这样:

class FeaturePresenter {
    private FeatureView featureView;
    private String someId;

    @Inject
    public FeaturePresenter(FeatureView featureView, @Named("someId") String someId) {
        this.featureView = featureView;
        this.someId = someId;
    }

    public void doNothing() {
        featureView.doNothing();
    }
}

现在我们已经看到,通过AndroidInjection.inject(this)调用注入活动是不可能传递参数的。那么我们如何使图形知道这个ID?这有点棘手,可能实际上是一个黑客,但目前一种方法是将ID保存在活动中的一个字段中,然后FeatureModule提供它。

1.首先我们将ID保存在一个字段中 FeatureActivity

public class FeatureActivity extends AppCompatActivity implements FeatureView {
    @Inject FeaturePresenter presenter;

    String someId;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        someId = getIntent().getStringExtra(EXTRA_SOME_ID);
        AndroidInjection.inject(this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        presenter.doNothing();
    }

    @Override
    public void doNothing() {

    }
}

然后我们添加一个提供方法 FeatureModule

@Module
public abstract class FeatureModule {
    @Binds
    abstract FeatureView provideFeatureView(FeatureActivity featureActivity);

    @Provides @Named("someId") static String provideSomeId(FeatureActivity featureActivity) {
        return featureActivity.someId;
    }
}

如前所述,这是可能的,因为FeatureActivity已经在图中。

注入活动以外
如本教程开头所述,Fragment注入以及其他Android组件不在本教程的范围之内。在官方文件涵盖了片段下喷吹片段的对象,我强烈建议你阅读它。还有更多关于服务和接收器的信息。

结论
新的dagger-android模块正在使我们更接近于适当的依赖注入,尽管在我看来,它比以前更复杂。
匕首团队正在积极致力于进一步简化,我不用怀疑API将尽快进行其他更改。
在撰写本教程之前,还没有提供官方样品。
您可能想要查看

原文链接
[https://android.jlelse.eu/android-and-dagger-2-10-androidinjector-5e9c523679a3]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容