Dagger2实战讲解

来源:互联网 发布:焊接数值模拟软件 编辑:程序博客网 时间:2024/05/21 18:34

  如果你在乎代码的可维护性,如果你注重代码的优雅度,如果你想让自己的代码趋于艺术品,那么你该好好沉思下了。说的不够明确? 那补充几个问题:项目现在处于何种境地?每天都在加班改bug?没按时交付产品? 呃…好像是。我们种种抱怨是产品需求无止境变化,这个确实算是一个导致延迟交付的因素,但是少年我们是不是也该自己反省一下呢,我们真的会conding么?还是coding对于我们来说就是复制粘贴?我想大家都明白。如果是后者,那你确实该好好反省了。实际项目中我们该当如何远离焦油坑、按时交付产品、受老板重视、工资翻倍、迎娶白富美、走上人生巅峰呢?貌似又不是一两篇文章能够传道解惑的,所以我自己也在不断的学习,与此同时我会把自己学习经历记录下来,希望能给部分同学些许指引。作为本系列首篇文章,文感若是不佳,意见尽管提,不要骂我就好!

  本小篇文章主要涉及代码解耦,核心技术:控制反转(Inversion of Control,英文缩写为IoC),可能第一次听到这词有些晦涩难懂,什么叫做反转呀,其实我初次见它也是一头雾水,只是后来用的多了,渐渐的也就理解了它,虽有在项目中的实践经验但文章也难免有措辞不当之处,如果说的和你之前理解的有什么出入还请不吝指正。

  实际开发项目中都是由两个或是更多的实体通过彼此的合作来实现业务逻辑,这就使得每个对象都需要它所依赖的对象的引用。如果这个获取依赖对象引用的过程要靠自身来实现,那么如你所见,这将导致代码高度耦合并且难以测试。该如何消除这种高度耦合问题呢?一般应用控制反转,对象在被创建的时候,由一个容器(调控系统)内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖对象的引用被注入到引用对象中。所以,控制反转的含义是:关于一个对象如何获取他所依赖的对象的引用,这个责任的反转。
  表述文字稍显苍白,你肯定也这么觉得,代码也许更容易使人理解,示例代码如下:

/** * Created by Chris Kyle on 2016/1/27. */public class HomeActivity extends Activity {    HomePresenter homePresenter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        homePresenter = new HomePresenterImpl();    }}

HomeActivity所依赖对象homePresenter的引用由本身获取,很显然这种高耦合关系使我们想更改HomePresenter的其它实现动作比较大,而这种做法在项目中却司空见惯了,因为我们已经习惯这种硬初始化方式,所以并没有觉得有什么不妥的地方。我现在告诉你?如果你的项目代码皆是如此是非常危险的!耦合过重导致我们大多情况下修改代码都是牵一发而动全身,不免会出现各种问题,而这些问题会严重拖长我们交付产品的工期。既然明确了耦合问题的严重性,那该如何消减耦合呢? 有些人会提出可以维护一个Factory,如以下代码:

/** * Created by Chris Kyle on 2016/1/27. */public class HomePresenterImplFactory {  public static HomePresenter createHomePresenter(String condition){        if ("A".equals(condition)){           return new HomePresenterAImpl();        }else if ("B".equals(condition)){            return new HomePresenterBImpl();        }else {            return new HomePresenterDefaultImpl();        }    }}

  看似削减了耦合关系,但是实质上却没有区别,耦合依然严重!很显然这不是我们想要的消减耦合的方案,我们希望的是HomeActivity不用过多去操心获取HomePresenter的实现的引用,你是否和我一样认为如果这种依赖关系能在一个配置文件中配置就好了,我们能想到的,肯定前人也都已经想到,而且做的还很不错!众所周知Spring框架的核心思想就是IoC,类似的还有JBossEJB等,有兴趣的可以去学习下。IoC是一个很大的概念,可以用不同的方式来实现。其主要实现方式有两种:依赖查找(Dependency Lookup)类似与JDNI的实现,通过JNDI来找到相应的业务对象(HomePresenterImpl),另一种是依赖注入(Dependency Injection),通过IoC容器将业务对象的引用注入到组件中。依赖查找据我所知没有比较出众的实现框架,而依赖注入用的范围比较广,所以依赖查找本文不再做概述。在Android中如何使用依赖注入进行模块解耦呢?是否还像Spring一样需要在xml文件中配置依赖呢?带着这些疑问我们走向了Dagger2,名字就很酷,匕首--锐利的匕首用来削减代码的耦合。

  为了介绍dagger2,不得不插个依赖小知识片段。Annotation(注解)是JDK1.5及以后版本引入的。它可以用于创建文档,跟踪代码中的依赖性,甚至执行基本编译时检查。注解是以‘@注解名’在代码中存在的,根据注解参数的个数,我们可以将注解分为:标记注解、单值注解、完整注解三类。它们都不会直接影响到程序的语义,只是作为注解(标识)存在,我们可以通过反射机制编程实现对这些元数据(用来描述数据的数据)的访问。另外,你可以在编译时选择代码里的注解是否只存在于源代码级,或者它也能在class文件、或者运行时中出现(SOURCE/CLASS/RUNTIME),很多开源框架都是利用注解使代码更加简洁,例如ButterKnifeEventBus等。想要用好Dagger2这个工具,以下这些注解必须要清楚它们的用途。

@Inject: 在需要依赖的地方使用这个注解。换句话说,你用它告诉Dagger2这个类或者字段需要依赖注入。之后Dagger2就会构造一个这个类的实例并满足他们的依赖。

@Module: modules的一个重要特征是它们设计为分区并组合在一起类里面的方法专门提供依赖,所以我们定义一个类,用@Module注解,这样Dagger2在构造类的实例的时候,就知道从哪里去找到需要的依赖。modules的一个重要特征是它们设计为分区并组合在一起,就是说我们的项目中模块中可以声明依赖几个被@Module注解的module。这样更容易把模块的粒度调制最优。
@Provide: 在modules中,我们定义的方法是用这个注解,以此来告诉Dagger2我们想要构造对象并提供这些依赖。

@Component: Components从根本上来说就是一个注入器,也可以说是@Inject和@Module的桥梁,它的主要作用就是连接这两个部分。 Components可以提供所有定义了的类型的实例,比如:我们必须用@Component注解一个接口然后列出所有的@Modules组成该组件,如果缺失了任何一块都会在编译的时候报错。所有的组件都可以通过它的modules知道依赖的范围。

@Scope: Scopes可是非常的有用,Dagger2可以通过自定义注解限定注解作用域。因为如同使用类不需要依赖对象如果被初始化一样,依赖对象的作用域也不想去控制,我们可以自定义的@PerActivity、PerFragment等注解一个类,所以这个对象的生命周期就和activity的一样。简单来说就是我们可以定义所有范围的粒度。

Dagger2示例

  理解它们的职责之后,如何实践才是最重要的,下面的示例也许有助于你更好的理解它,首先我们修改HomeActitity的实现,除去它现有的耦合。如以下代码:

/** * Created by Chris Kyle on 2016/1/27. */public final class HomeActivity extends BaseActivity<HomeView, HomePresenter> implements HomeView {    HomeComponent homeComponent;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_home);        ButterKnife.bind(this);    }    @Override    public void injectDependencies() {        homeComponent = DaggerHomeComponent.builder().                homeModule(new HomeModule()).                build();        homeComponent.inject(this);    }    @NonNull    @Override    public HomePresenter createPresenter() {        return homeComponent.presenter();    }    @Override    public Context getContext() {        return this.getApplicationContext();    }}

我想你肯定是一头雾水,不知道这段代码的工作流程是怎么样的,那就一步步分析吧,首先声明一下injectDependencies()是在BaseActivity的onCreate()中调用的,也就是说我们必须要完成对象图的构建蓝图;接着看下homeModule(new HomeModule()),HomeModule是我们提供依赖的地方,代码如下:

/** * Created by Chris Kyle on 2016/1/27. */@Modulepublic class HomeModule {    @Provides    HomePresenter provideMainPresenter() {        return new HomePresenterImpl();    }}

按照Dagger2的约定,必须使用@Module注解修饰该类,provideMainPresenter()是具体提供依赖的函数,也按约定使用@Provides注解表示它要提供依赖,这里是提供HomePresenterImpl。
已经告诉Dagger2谁为HomeActivity提供依赖了,我们还需要告诉它去哪找依赖了,也就是HomeComponent的职责所在,看下具体代码吧。

/** * Created by Chris Kyle on 2016/1/27. */@Component(modules = {HomeModule.class})public interface HomeComponent {    void inject(HomeActivity homeActivity);    HomePresenter presenter();}

@Componen注解,指明提供依赖的modules。接着就是inject了,需要注入到哪就在inject传入,参数不能为抽象,而必须是具体的类,比如这里的HomeActivity,如果写成Activity是编译不成功的。

到这里Dagger2让我们做的工作都已经做完了,剩下的工作都交给Dagger2吧!之后我们可以获取HomePresenter实现不再是直接new HomePresenterImpl;,而是

    @Override    public HomePresenter createPresenter() {        return homeComponent.presenter();    }

如果后期要更改HomePresenter的实现类,我们不再需要修改HomeActivity中的代码,而是修改与业务无关的HomeModule,这样就达到解耦的目的了。
本小篇文章就算完了,想要进一步学习如何削减程序的耦合问题,那就关注我吧 (^__^)

4 1
原创粉丝点击