Dagger2 这次入门就不用放弃了

Dagger2 这次入门就不用放弃了

前言

之前也研究过很多次Dagger2这东西了,可能以后RxJava+Retrofit+MVP+Dagger2Android发展的主流框架,看了Dagger2的实现代码,有点不明所以。上网也有很多文章介绍依赖注入、Dagger2的组件等等那些,这样这样这样什么组件呀、模块呀、注入呀。但是感觉对于入门来说那些文章都没有说到点子上,具体怎么用这个核心点而且应该怎么写代码?为什么这样写,并没有很明确的说明。我来回看了几遍代码之后,总结出了一点经验,不知道说的对不对。

没有了解过Android MVP结构的同学可能不利于阅读。

为什么使用Dagger2

对于这个问题我也困惑了很久,Java代码就是这样写,并没有考虑过依赖注入是什么鬼,并且依赖注入有什么不好。这篇文章详细介绍了依赖注入,感兴趣的可以传送过去看看。

简单来说,依赖注入就是为了控制反转和解耦的,这些高深的名词儿可能一时也不懂。不要紧,我举个栗子就能明白了,请看代码:

class A{

}

class B{
    A a;
    public B(){
        a = new A();
    }
}

上面的代码很简单,class B持有一个class A的对象,然后假如根据业务需求需要修改A类的某些实现,这样的话就需要修改B类中的创建A对象的方式。假想一下,当你的代码规模达到一定的程度的时候,需要改一部分代码,牵一而发动全身,需要改的代码量多,而且容易出错。还有一个不好的情况就是,当要对A进行单元测试的时候,就要测试B,这样的耦合可能不是程序员希望看见的。Dagger2就是为了解决这样的问题而出现的。这里只是一个简单的例子,可能描述依赖注入的原理不是很清晰,如果不是很了解的话可以从网上搜索出很多文章。

Dagger2的配置

  • 目录添加apt支持,apt是用于自动生成代码来进行依赖注入的。

项目中的build.gradle添加:

dependencies {
        classpath ‘com.android.tools.build:gradle:2.1.0‘
        classpath ‘com.neenbedankt.gradle.plugins:android-apt:1.4‘
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
  • modulebuild.gradle添加:
apply plugin: ‘com.neenbedankt.android-apt‘

    android{
        ...
    }

dependencies {
    provided ‘org.glassfish:javax.annotation:10.0-b28‘
    compile ‘com.google.dagger:dagger:2.5‘
    compile ‘com.google.dagger:dagger-compiler:2.5‘
}

例子

这里通过一个例子来向Activity注入一些成员变量。(例子代码来自网上),来说明Dagger2的基本使用。

例子使用的是MVP模式,内容是通过注入一个Presenter,然后通过Presenter来设置TextView显示内容为user.name;

其中User的代码如下:

public class User {
    public String name;

    public User(String name) {
        this.name = name;
    }
}

Presenter的代码:

public class DaggerPresenter {
    DaggerActivity activity;
    User user;

    public DaggerPresenter(DaggerActivity activity, User user) {
        this.user = user;
        this.activity = activity;
    }

    public void showUserName() {
        activity.showUserName(user.name);
    }
}

现在的场景是有一个DaggerActivity,里面持有一个DaggerPresenter的成员,我们该如何使用Dagger2来注入这个成员呢?

第一步:编写Module

我这里编写了一个ActivityModule,代码如下:

@Module
public class ActivityModule {
    private DaggerActivity activity;

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

    @Provides
    public DaggerActivity provideActivity() {
        return activity;
    }

    @Provides
    public User provideUser() {
        return new User("user form ActivityModule");
    }

    @Provides
    public DaggerPresenter provideDaggerPresenter(DaggerActivity activity, User user) {
        return new DaggerPresenter(activity, user);
    }
}

首先这里编写有一些规则的,类需要用@Module注解来标示,可以看到我这个AcitivtyModule中定义了一个构造函数,需要传进来一个DaggerActivity对象。

首先我们需要明确一个点,就是Module的作用是用来提供生成依赖对象的,比如我要注入DaggerPresenter,那么这个Module的作用就是需要生成一个DaggerPresenter的对象,来让Dagger2注入到DaggerActivity中。

所以我们这里需要编写一个函数provideDaggerPresenter,这个函数可以从上面的代码看出,我们需要对这个函数使用@Provides注解,然后,我们这里需要传入两个参数,一个DaggerActivity,一个User对象。那么,这两个参数从何而来呢?

细心的同学可能会发现,我上面的代码中还定义了两个函数,分别为provideUserprovideActivity,大家猜出点什么没有(嘿嘿),这里provideDaggerPresenter的两个参数就是通过这两个函数来获取的。如果没有声明这两个函数的话,可能编译期间会报错哟。通过上述内容,各位同学应该明白了Module应该如何编写了吧。

编写Module有以下几个注意点:

  • 类需要用@Module来标明注解
  • 这里有一点规则,用@Provides注解的函数需要以provide开头,然后后面接什么内容都可以,看自己喜欢,事实上,经过我的测试,我把provideActivity()改成provideA()同样是可以注入成功的,所以大家可以知道,这里是根据返回值类型来标识的,方法名并不重要,只需要保证以provide开头即可。

第二步:编写ActivityComponent

请看代码:

@Component(modules = ActivityModule.class)
public interface ActivityComponent {
    void inject(DaggerActivity daggerActivity);
}

这里的代码够少吧,哈哈,我们编写的这个Component需要用@Component注解来标识,同时声明了modules为上面编写的ActivityComponent,然后提供了一个方法,叫做inject,用来在Activity中注入。(这里为什么要写一个方法叫做inject我暂时还没弄清楚,改名字是可以的,但是参数类型不能改,并且一定要指定module=ActivityModule才能注入),这里我们暂且理解为提供一个方法来注入对象吧。

第三步:AndroidStudio -> Build -> Make Project

写到这里的时候就可以Make Project了,完成之后apt会自动生成一个以Dagger开头的Component,比如,我们上面写的是ActivityComponent,生成了类名就为DaggerActivityComponent。这个类我们可以直接使用。

第四步,注入Activity中

在第三步中已经生成了一个DaggerActivityComponent了,我们在ActivityonCreated函数中编写如下代码:

 DaggerActivityComponent.builder()
    .activityModule(new ActivityModule(this))
    .build()
    .inject(this);

可以看到我们首先调用这个了类的builder(),然后调用一些方法。这些方法也有一些规律噢,比如我们的ActivityComponent指定的moduleActivityModuleDaggerActivityComponent就会有一个名为activityModule的方法,我们需要调用它,并传入参数,这里我们直接new了一个ActivityModule进去。

好了,到此为止,我们已经使用Dagger2形成了关联,我们还需要注入Presenter。在Activity中:

@Inject
DaggerPresenter presenter;

我们直接使用注解@Inject就可以对这个成员进行注入了。

下面是我的Activity的完整代码:

public class DaggerActivity extends AppCompatActivity {

    private static final String TAG = "DaggerActivity";
    TextView text;

    @Inject
    DaggerPresenter presenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dagger);
        text = (TextView) findViewById(R.id.text);
        inject();
        presenter.showUserName();
        //Log.i(TAG, "client = " + (client == null ? "null" : client));
    }

    private void inject() {
        DaggerActivityComponent.builder().activityModule(new ActivityModule(this))
                .build().inject(this);
    }

    public void showUserName(String name) {
        text.setText(name);
    }
}

上面的代码运行起来的结果就是在DaggerActivityTextView中显示了一串字符串"user form ActivityModule",虽然例子简单,但是基本上实现了简单依赖注入,希望对于Dagger2的入门有点启发。

好啦,现在我们的项目又有新需求了,我们希望提供一个全局的OkHttpClientRetrofit对象来进行网络请求,他的生命周期是和APP一致的,这个时候我们就需要定制AppComponent了。

首先我们按照老规矩,第一步先编写Module,一下是ApiModule

@Module
public class ApiModule {
    public static final String END_POINT = "http://www.baidu.com";

    @Provides
    @Singleton
    OkHttpClient provideOkHttpClient() {
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(60 * 1000, TimeUnit.MILLISECONDS)
                .readTimeout(60 * 1000, TimeUnit.MILLISECONDS)
                .build();
        return client;
    }

    @Provides
    @Singleton
    Retrofit provideRetrofit(OkHttpClient client) {
        Retrofit retrofit = new Retrofit.Builder()
                .client(client)
                .baseUrl(END_POINT)
                .build();
        return retrofit;
    }

    @Provides
    @Singleton
    User provideUser(){
        return new User("name form ApiProvide");
    }

}

请注意,我这里的provide方法额外添加了一个@SingleTon注解,这里说明是全局单例的对象,而且我这里改动了一小部分代码,把ActivityModuleprovideUser移动到这里来了,我这里是为了演示依赖过程。

接下来编写AppComponent了:

@Singleton
@Component(modules = {ApiModule.class})
public interface AppComponent {

    OkHttpClient getClient();

    Retrofit getRetrofit();

    User getUser();
}

这里的AppComponent提供了3个方法,分别用来暴露OkHttpClientRetrofitUser对象的,这里暂且不提为什么要暴露,大家别急,继续往下看。

第三部就是Make Project了,之后就会生成一个叫做DaggerAppComponent的类,之后我们在MyApplicaiotn中实例化这个Component:

public class MyApplication extends Application {

    AppComponent appComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        appComponent = DaggerAppComponent.builder()
                .apiModule(new ApiModule())
                .build();
    }

    public AppComponent getAppComponent() {
        return appComponent;
    }

}

这里别忘了在AndroidManifest中设置为自定义的MyApplicaiton哦。上面的代码很简单,我们只是实例化了一个AppComponent,然后提供了一个方法用于获取这个Component

然后我们需要修改一下ActivityComponent,改成下面这样:

@ActivityScope
@Component(modules = ActivityModule.class,dependencies = AppComponent.class)
public interface ActivityComponent {
    void inject(DaggerActivity daggerActivity);
}

改动的地方呢是添加了一个@ActivityScope然后,添加了一个dependencies = AppComponent.class。没错,Component之间也可以依赖的。

解释一下这个ActivityScope,这里查询了网上的资料之后,据说是可以和Activity的生命周期绑定,没有声明这个注解的话编译会报异常。我暂时无法对这个Scope理解清晰,不做评论。

@Scope
public @interface ActivityScope {
}

最后一步啦,改动DaggerActivity

public class DaggerActivity extends AppCompatActivity {

    private static final String TAG = "DaggerActivity";
    TextView text;

    @Inject
    DaggerPresenter presenter;

    @Inject
    OkHttpClient client;

    @Inject
    Retrofit retrofit;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dagger);
        text = (TextView) findViewById(R.id.text);
        inject();
        presenter.showUserName();
        Log.i(TAG, "client = " + (client == null ? "null" : client));
        Log.i(TAG, "retrofit = " + (retrofit == null ? "null" : retrofit));
    }

    private void inject() {
        AppComponent appComponent = ((MyApplication) getApplication()).getAppComponent();
        DaggerActivityComponent.builder()
                .appComponent(appComponent)
                .activityModule(new ActivityModule(this))
                .build().inject(this);
    }

    public void showUserName(String name) {
        text.setText(name);
    }
}

可以看到我这里添加了两个注入,分别注入了一个OkHttpClient和一个Retrofit对象,然后在注入的时候也把AppComponent也添加进来了。然后我们先看运行结果,后面我会解释一下整个依赖关系。

运行结果:

Log输出:

    07-13 12:24:46.433 12424-12424/com.sample I/DaggerActivity: client = [email protected]
    07-13 12:24:46.433 12424-12424/com.sample I/DaggerActivity: retrofit = [email protected]

然后在手机上运行的话,TextView会显示"name from ApiProvide",从结果看来我们已经成功注入了这3个对象。

现在估计大家有些疑问。

  • 首先我们看回ActivityComponent
@Module
public class ActivityModule {
    private DaggerActivity activity;

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

    @Provides
    public DaggerActivity provideActivity() {
        return activity;
    }

    @Provides
    public DaggerPresenter provideDaggerPresenter(DaggerActivity activity, User user) {
        return new DaggerPresenter(activity, user);
    }
}

这里的provideUser方法已经去掉了,那么根据我前面说的话,那我们需要从哪里获取这个User对象呢。我们看回前面的:

@ActivityScope
@Component(modules = ActivityModule.class,dependencies = AppComponent.class)
public interface ActivityComponent {
    void inject(DaggerActivity daggerActivity);
}

可以看到这个ActivityComponent是依赖AppComponent的,AppComponent中定义了3个方法:

     OkHttpClient getClient();

    Retrofit getRetrofit();

    User getUser();

分别用来提供这三个对象的,这样就可以解释清楚了,他们存在依赖关系,就像我们对象之间的继承一样,值得注意的是这三个方法也是根据返回值类型来识别的,他们会分别找到AppComponent中的module(ApiModule)中的provide方法来获取对象。

这里我们发现一个有趣的现象,首先我们提供这三个方法可以被Activity的成员变量注入(可以看到,我们成功的注入的OkHttpClientRetrofit),同时也可以让被依赖的Component(ActivityComponent)所使用.

如果我们不把这三个对象声明在AppComponent中,在编译的过程中就会报异常。在专业术语好像叫做:暴露给子图?

结论

我这里只是对于怎么使用Dagger2来了一个流程,并且做出了一些通俗化的解释。听到很多人说这个Dagger2入门困难,可能是因为需要理解完Dagger2通过APT生成的代码的流程才能完全理解吧。但是我们通常学习一个框架是学会怎么使用,使用过了之后,才会对它的原理进行了解,然而Dagger2的使用起来也并不简单,对于一个没有接触过Dagger1,又没有了解过依赖注入的概念的人来说,一下子需要看明白还是有点难度的。我也是经历了很多次入门到放弃,感觉自己现在也是理解的不太清晰,其实都是猜的(嘿嘿)。总之这篇文章的着重点是为了让大家知道如何使用Dagger2,并没有解释过内部的原理,但是希望这些东西能带给一些想入门Dagger2又感觉难以理解的人一点点启发吧。

Demo源码:https://github.com/Jamlh/Sample/tree/master/app/src/main/java/com/sample/dagger

时间: 2024-11-05 10:08:41

Dagger2 这次入门就不用放弃了的相关文章

ES6 从6入门到10放弃

ES6 从6入门到10放弃(耐心) 语法本身并不重要,怎么解决问题!怎么更好的解决问题才是本质 ES6 --> ES7.8.9.10 利用新技能拓宽解决问题的思路 全新的JavaScript体系 字符串模板(直接赋值变量,支持表达式) 监听数据 自定义数据结构,遍历 ES7 --> ES10 全新体系,改变JS书写习惯 学习方法 体验乐趣 1天 进入状态 10天 养成习惯 1个月后 收获知识 3个月后 行程思维习惯 5个月后 ES6 基础语法 _作用域 Let & Const 全局作用

Dagger2从入门到放弃再到恍然大悟

现在Dagger2在项目里用的越来越多了,最近花了些时间学习了一下Dagger2,这篇文章主要帮助理解Dagger2的注入实现过程,如有错误,还请指正! 什么是Dagger2 Dagger2是Dagger的升级版,是一个依赖注入框架,现在由Google接手维护. 恩,这里有个关键字依赖注入,因此我们得先知道什么是依赖注入,才能更好的理解Dagger2. 依赖注入是面向对象编程的一种设计模式,其目的是为了降低程序耦合,这个耦合就是类之间的依赖引起的. 举个例子:我们在写面向对象程序时,往往会用到组

python仙修之 入门之后不放弃?

不以学习为目的的学习,那是耍流氓~所以流氓都成了有钱人...... 额,不贫了.从某天开始,次博客只谈学习和感情,不谈工作,不扯没用的~ 今天整个啥呢?容我思量思量~嗯,昨天不整了数据库吗,那就从这里开始吧,来粘贴个小代码,小爽一下子~ """数据库の基本操作""" from pymysql import *from hashlib import *from pymongo import *from redis import * def regi

Dagger2 使用正确姿势。

Dagger2 使用正确姿势. 上一篇文章<Dagger2 这次入门就不用放弃了>中介绍了Dagger2的一些显浅的使用方式,我觉得是非常适合入门Dagger2的傻瓜式讲解,后来发现有些内容讲的不够仔细,有些细节没有详细解释清楚.参考了以下三篇文章后,对之前的内容进行一些补充. Android:dagger2让你爱不释手-基础依赖注入框架篇 Android:dagger2让你爱不释手-重点概念讲解.融合篇 Android:dagger2让你爱不释手-终结篇 以上这三篇文章对于Dagger2的思

关于dagger2

解决Studio3.0 Dagger2注入Error:android-apt plugin不兼容的问题 https://blog.csdn.net/hanfengzqh/article/details/78487169?locationNum=3&fps=1 天在导入Google官方推荐Dagger2注入框架时出现一个错误: Error:android-apt plugin is incompatible with the Android Gradle plugin.  Please use '

JavaScript - 收藏集 - 掘金

Angular 中的响应式编程 -- 浅淡 Rx 的流式思维 - 掘金第一节:初识Angular-CLI第二节:登录组件的构建第三节:建立一个待办事项应用第四节:进化!模块化你的应用第五节:多用户版本的待办事项应用第六节:使用第三方样式库及模块优化用第七节:给组件带来活力Rx--隐藏在 Angular 中的利剑Redux你的 A... Electron 深度实践总结 - 前端 - 掘金思维导图 前言: Electron 从最初发布到现在已经维护很长一段时间了,但是去年才开始慢慢升温.笔者个人恰好

上篇 | 大数据公司挖掘数据价值的49个典型案例(值得收藏)

导读:本文是近年来不同行业.不同领域的大数据公司的一些经典案例总结.尽管有些已经是几年前的案例,但其中的深层逻辑对于未来仍有启发. 本文力图从企业运营和管理的角度,梳理出发掘大数据价值的一般规律:一是以数据驱动的决策,主要通过提高预测概率,来提高决策成功率:二是以数据驱动的流程,主要是形成营销闭环战略,提高销售漏斗的转化率:三是以数据驱动的产品,在产品设计阶段,强调个性化:在产品运营阶段,则强调迭代式创新. 01 上篇:天然大数据公司的各种套餐 从谷歌.亚马逊.Facebook.LinkedIn

再看hadoop PRC

以前写过一篇文章叫做<hadoop RPC从入门到暂时放弃>,现在粗略的把<Hadoop 2.xHDFS源码剖析>看完了第一遍,感觉对HDFS的整体了解上升了一个层次,并且重新看了一下动态代理.protocol buffer所以这次重新来写写对于hadoop RPC的认识. 下面还是主要通过ClientProtocol协议,即hdfs客户端与NameNode通信的协议,来介绍一下对hadoopRPC的认识. 客户端 首先编写程序操作hadoop hdfs的时候,通常都是会用到DFS

寒门再难出贵子

在越来越看清楚“性格决定命运”,性格这东西是熔透与骨髓的,性格的养成和学校教育没有多大关系,大多决定与家庭背景,和成长环境...从大学毕业出来的第一步往往起到至关作用的是家庭背景,也就是从起跑线普通家庭的孩子就输了一大截..  在一个物欲横流的时代,当金钱决定一切,成为人得终极信仰的时候,这时候也是“门阀”,阶层相应出现的年代. 结合我自己近半年来的观察, 我在商业银行人力资源部上班,去年年招了很多学校的实习生,实习可不是正式录用了,以前自己年龄也相对年轻,没有太多关注以往的实习生,今年正好我负