Dagger2使用

作者: YoKey | 来源:发表于2015-12-01 17:48 被阅读63588次

在简单使用了一段时间的dagger2之后,来谈谈对dagger2浅薄的认知。

首先,使用依赖注入可以带来哪些好处?

1、依赖的注入和配置独立于组件之外,注入的对象在一个独立、不耦合的地方初始化,这样在改变注入对象时,我们只需要修改对象的实现方法,而不用大改代码库。

2、依赖可以注入到一个组件中:我们可以注入这些依赖的模拟实现,这样使得测试更加简单。

3、app中的组件不需要知道有关实例创建和生命周期的任何事情,这些由我们的依赖注入框架管理的。

我觉得,dagger2这样的依赖注入框架对MVP架构来说,是最好的解耦工具,可以进一步降低modle-view-presenter之间的耦合度。
所以,如果你的项目在使用MVP架构开发,强烈建议配合dagger2一起使用。

接下来,在贴代码之前,我先说说明下我的MVP架构和传统的MVP有些不同,传统MVP的M层处理业务逻辑,P层仅仅是V和M的桥梁;而我的P层同时处理与model相关的业务逻辑,不处理View层次的逻辑,View层次的逻辑交给V自己处理,M层仅仅是bean,这种方式是根据开发中的实际情况而作的考虑,这里先不作讨论。

先看结构图:


接下来,分解这张图:
AppComponent: 生命周期跟Application一样的组件。可注入到自定义的Application类中,@Singletion代表各个注入对象为单例。

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
    Context context();  // 提供Applicaiton的Context

    ThreadExecutor threadExecutor();   // 线程池

    ApiService apiService();  // 所有Api请求的管理类

    SpfManager spfManager();  // SharedPreference管理类

    DBManager dbManager();  // 数据库管理类
}

AppModule: 这里提供了AppComponent里的需要注入的对象。

@Module
public class AppModule {
    private final MyApplication application;

    public AppModule(MyApplication application) {
        this.application = application;
    }

    @Provides
    @Singleton
    Context provideApplicationContext() { 
       return application;
    }

    @Provides
    @Singleton
    ThreadExecutor provideThreadExecutor(JobExecutor jobExecutor) {
        return jobExecutor;
    }

    @Provides
    @Singleton
    ApiService providesApiService(RetrofitManager retrofitManager) {
        return retrofitManager.getService();
    }

    @Provides
    @Singleton
    SpfManager provideSpfManager() {
        return new SpfManager(application);
    }

    @Provides
    @Singleton
    DBManager provideDBManager() {
        return new DBManager(application);
    }
}

这里细心的童鞋可能发现,为何有些方法直接返回入参,有些需要返回一个new的对象呢?
这里如果对DBManager的写法换成:

DBManager provideDBManager(DBManager dbManager) {
  return dbManager;
}

这样编译不会通过,会报一个循环依赖的错误,这种写法需要在返回参数和入参是继承关系才可以。感兴趣的可以查看dagger2生成的代码。

对于直接返回的类JobExecutor、RetrofitManager,它们类的构造函数一定要加上@Inject的注解:

@Inject
public JobExecutor() {
    // 初始化
    // ......
}

接下来谈谈ActivityComponent,可以看到有个@ActivityScope注解,这个注解是自定义的,对应Activity的生命周期,Dagger2可以通过自定义注解限定注解作用域。

@Scope
@Retention(RUNTIME)
public @interface ActivityScope {}

ActivityComponent:生命周期跟Activity一样的组件,这里提供了inject方法将Activity注入到ActivityComponent中,通过该方法,将Activity中需要注入的对象注入到该Activity中。

@ActivityScope
@Component(dependencies = AppComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
    Activity activity();

    void inject(LoginActivity loginActivity);

    void inject(MainActivity mainActivity);

    // ....
}

ActivityModule:注入Activity,同时规定Activity所对应的域是@ActivityScope

@Module
public class ActivityModule {
    private final Activity activity;

    public ActivityModule(Activity activity) {
        this.activity = activity;
    }

    @Provides
    @ActivityScope
    Activity activity() {
        return this.activity;
    }
}

至此,注入工作初步完毕了,看到这里,可能有童鞋有疑问,Presenter(或者Biz)的注入在哪里,为何没在ActivityComponent里?
是的,正常来说,结构图应该是下面这张图的样子:


我建议使用这种方式,对于不同的Activity,创建各个对应的ActivityCompontent,同时把Presenter(Biz)注入到Component的视图中,这也是dagger2推荐的做法,Dagger 2希望使用@Component注解接口将依赖关系链接起来。

而我的做法没有把Presenter注入到ActivityComponent中,因为Presenter的作用域和Activity一样,好处是节省代码(- -),大家可以根据项目情况自行选择注入方式。

使用:

public class LoginActivity extends BaseActivity implements LoginView, ValidCodeView {
    @Inject LoginPresenter loginPresenter;

    @Inject ValidCodePresenter validCodePresenter;

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        initInject();
        // 此处省略N行代码
    }

    private void initInject() {
        // 构建Component并注入
        getActivityComponent().inject(this);
        loginPresenter.attachView(this);
        validCodePresenter.attachView(this);
    }

    //  建议写在基类Activity里
    protect ActivityComponent getActivityComponent(){
          return  DaggerActivityComponent.builder()
                      .appComponent(getAppComponent())
                      .activityModule(getActivityModule())
                      .build();
    }

    //  建议写在基类Activity里
    protect ActivityModule getActivityModule(){
          return new ActivityModule(this);
    }

    // 建议写在MyApplication类里
    public AppComponent getAppComponent(){
         return DaggerAppComponent.builder()
         .appModule(new AppModule((MyApplication)getApplicationContext()))
         .build();
    }
}

其中LoginPresenter:

@ActivityScope
public class LoginPresenter extends DefaultMvpPresenter<LoginView, RESTResult<UserVO>> {
     // 此处省略

    @Inject
    public LoginPresenter(ApiService apiService, ThreadExecutor jobExecutor, SpfManager spfManager) {
        this.apiService = apiService;
        this.jobExecutor = jobExecutor;
        this.spfManager = spfManager;
    }
    public void login(String mobile, String code) {
          // todo
    }
}

这样,dagger2的简单使用就介绍完毕了,如果有对一些基础概念不是很理解的童鞋,可以查看官方文档

相关文章

网友评论

  • 629d3e4ca122:为什么我make project 时 报错:
    android.app.Application cannot be provided without an @Inject constructor or from an @Provides-annotated method.
    android.app.Application is injected at
    com......di.module.ClientModule.provideClient(application, …)
  • 小小程序员jh:加1,学习下。
  • 戴定康:后面两段晦涩难懂
  • boboyuwu:请教下楼主哈 为啥构造方法上面可以用Inject不能用Qualifier?
  • wowling:感谢感谢!
  • woniu0936:请问下面代码中,写的这些方法的作用是什么?在AppModule中已经写了他们的provide方法,在使用@inject标记一个对象时,会自己去appModule中找需要的东西,为什么还要在AppComponent在申明一遍这些方法?不声明这些方法,也能找到提供的这些对象啊:
    @singleton
    @Component(modules = AppModule.class)
    public interface AppComponent {
    Context context(); // 提供Applicaiton的Context

    ThreadExecutor threadExecutor(); // 线程池

    ApiService apiService(); // 所有Api请求的管理类

    SpfManager spfManager(); // SharedPreference管理类

    DBManager dbManager(); // 数据库管理类
    }
    YoKey:@woniu0936 更直观,更优雅,类似一个全局定义的桥梁声明类,这样在业务复杂的时候,component依然简洁,它负责暴露可注入的对象
    woniu0936:@YoKey 除过因为dagger推荐声明注入外,声明注入比不声明注入有什么好处或者说是不同呢?
    YoKey:@woniu0936 dagger推荐使用component来声明注入,不注入当然也是可以直接使用的~
  • bhfo:请问@ActivityScope,这个注解具体是怎样运作的?怎么加上这个就可以与Activity的生命周期绑定了?
    bhfo:@sunshine8 算懂了,实际还是由注射器来管理生命期的。
    信念着了火:@bhfo 你现在有答案吗?我也不清楚
  • AhaCat:大神能不能推荐一下氛围比较好的安卓论坛或者网站啊? :blush:
    YoKey: @AhaCat 简书~gankio~掘金…等等
  • yangyirunning:路过就顶,貌似现在用注解的越来越多了,得空研究一下 :heart_eyes:
  • fef3a2360375:https://github.com/CarlLu/MVPframe
    mvp+rxjava+retrofit+dagger2开源框架,希望大家支持
  • Android解忧杂货店:兄弟,能不能贴个github地址,研究一下代码mvp结构。。
    YoKey:@beyond小哥 这篇是我实际项目中的一些代码片段;推荐你一个项目,可以参考下https://github.com/CycloneAxe/phphub-android;dagger2注入方式有多种方式,最终都可以达到同样的目的;
    至于MVP,我建议从最基本的MVP入手,然后参考些mosby、nucleus之类的MVP封装项目进一步改造自己的MVP架构 :smile:
    Android解忧杂货店:@YoKey 嗯嗯,了解。那这篇文章的Demo还有吗?我刚刚开始学习使用Dagger2,准备用在项目中。有的话给个链接下载看看,或是发我邮箱:859358014@qq.com,万分感谢!
    YoKey:@beyond小哥 :) 地址就在我的简书主页介绍上~ 我的github上还没有MVP相关的DEMO,等以后有机会会分享的~ :joy:
  • HelloVass:今天又看了一遍,我很好奇,导致循环引用错误的原因是什么,右右能贴一下代码吗?
    YoKey:@HelloVass provideDbManager(xx)方法提供了一个Dbmanager对象,但是确依赖了自己(DbManager dbManager) 这样在编译时会报Dependency cycle错误
  • HelloVass:看了前面半段还以为右右要讲架构设计了,戛然而止,白开心了
    v3sona:@LCN 参考google 的 mvp dagger 写了个demo
    里面还用了retrofit2,okhttp3,RxJava,RxAndroid,ButterKnife等
    https://github.com/vsona/orz
    LCN:@YoKeyword 楼主可不可以贴个demo之类的参考下
    YoKey:@HelloVass 思路说了下啦 可以试试 目前经过了几次重构 感觉还行 有了Rx业务逻辑的处理和维护很爽 安卓里绝大多数情况没有特别复杂的业务逻辑 把M和P轻薄处理 感觉能满足我目前接触到的项目开发需求

本文标题:Dagger2使用

本文链接:https://www.haomeiwen.com/subject/inbrhttx.html