8年Android架构师:Dagger2使用详解

原文连接:www.jianshu.com/p/2cd491f0d…java

目录

  • 一:Dagger2是什么?
  • 二:为何要有Dagger2
  • 三:Dagger2如何使用
      1. 基本的概念
      1. 如何使用Dagger2
      1. 高级用法
      • (1)构造方法须要其余参数时候
      • (2) 模块之间的依赖关系
      • (3) @Named注解使用
      • (4) @Singleton注解
      • (5)自定义Scoped
      • (6)Subcomponent
      • (7)lazy 和 Provider
  • 四: MVP + Dagger2

一:Dagger2是什么?

是一个依赖注入框架,butterknife也是一个依赖注入框架。不过butterknife,最多叫奶油刀,Dagger2被叫作利器啊,他的主要做用,就是对象的管理,其目的是为了下降程序耦合。android

二:为何要有Dagger2

下面我就手写了git

public class A {
    public void eat() {
        System.out.print("吃饭了");
    }
}
复制代码

使用的时候咱们就要github

A a = new A();
a.eat();
复制代码

若是如今改了,早A的构造方法中必须传入B对象api

public class A {
    private B b;
    public A(B b) {
        this.b = b;
    }
    public void eat() {
        System.out.print("吃饭了");
    }
}
复制代码

那么使用的时候bash

A a = new A(new B());
a.eat();
复制代码

可能就有人说了,不就加一个对象么,这里只是我举的一个很简单的例子,看的感受很简单,可是在实际开发中,若是如今改了一个这个构造方法。是否是意味着,整个项目中的都的改,一不当心, 就是BUG 啊架构

三:Dagger2如何使用

1. 基本的概念

上来给你说,怎么玩,确定懵逼,这里我简单说一下几个概念,想有个认知,在往下看,会好不少,Dagger 是经过@Inject使用具体的某个对象,这个对象呢,是由@Provides注解提供,可是呢,这个@Provides只能在固定的模块中,也就是@Module注解,咱们查找的时候,不是直接去找模块,而是去找@Componentapp

咱们反向推导,当咱们使用框架

@Inject
A a
复制代码

想要获取a对象的示例的时候,Dagger2 会先去找,当前Activity或者Fragment所链接的桥梁,例如上图中,链接的只有一个桥梁,实际上能够有多个,这个桥梁,会去寻找他所依赖的模块,如图中,依赖了模块A,和模块B,而后在模块中,会去寻找@Providers注解,去寻找A的实例化对象。ide

2. 如何使用Dagger2

(1) 引入依赖库

Dagger2官网

compile 'com.google.dagger:dagger:2.11'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
复制代码

(2) 建立Moudule

//第一步 添加@Module 注解
@Module
public class MainModule {
}
复制代码

(3)建立具体的示例

//第一步 添加@Module 注解
@Module
public class MainModule {
    //第二步 使用Provider 注解 实例化对象
    @Provides
    A providerA() {
        return new A();
    }
}
复制代码

(4)建立一个Component

//第一步 添加@Component
//第二步 添加module
@Component(modules = {MainModule.class})
public interface MainComponent {
    //第三步  写一个方法 绑定Activity /Fragment
    void inject(MainActivity activity);
}
复制代码

(5)Rebuild Project

而后AS 会自动帮咱们生成一个

开头都是以Dagger开始的

(6)将Component与Activity/Fragment绑定关系

package com.allens.daggerdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.allens.daggerdemo.Bean.A;
import com.allens.daggerdemo.component.DaggerMainConponent;
import javax.inject.Inject;

public class MainActivity extends AppCompatActivity {
    /***
     * 第二步  使用Inject 注解,获取到A 对象的实例
     */
    @Inject
    A a;

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

        /***
         * 第一步 添加依赖关系
         */
        //第一种方式
        DaggerMainConponent.create().inject(this);

        //第二种方式
        DaggerMainConponent.builder().build().inject(this);

        /***
         * 第三步  调用A 对象的方法
         */
        a.eat();
    }
}
复制代码

确定有小伙伴说了,为了拿到一个对象,这么大个弯,太麻烦了。别急慢慢看,路要一步一步走嘛

3. 高级用法

(1)构造方法须要其余参数时候

怎么说呢,就和最来时的意思同样,

A a = new A(new B());
a.eat();
复制代码

这种状况,如何使用Dagger2呢 确定有小伙伴这么想

@Provides
    A providerA() {
        return new A(new B());
    }
复制代码

直接 new 一个B ,这样的使用方法,是不对的!!!!!!,不对的!!!!!!!,不对的!!!!!!!!!

正确的打开方式

这时候,咱们什么都不用改,只须要在moudule中添加一个依赖就能够了

@Module
public class MainModule {

    /***
     * 构造方法须要其余参数时候
     *
     * @return
     */
    @Provides
    B providerB() {
        return new B();
    }

    @Provides
    A providerA(B b) {
        return new A(b);
    }
}
复制代码

(2) 模块之间的依赖关系

模块与模块之间的联系

@Module (includes = {BModule.class})// includes 引入)
public class AModule {
    @Provides
    A providerA() {
        return new A();
    }
}
复制代码

这样的话,Dagger会如今A moudule 中寻找对象,若是没找到,会去找module B 中是否有被Inject注解的对象,若是仍是没有,那么GG,抛出异常

一个Component 应用多个 module

@Component(modules = {AModule.class,BModule.class})
public interface MainComponent {
    void inject(MainActivity activity);
}
复制代码

dependencies 依赖其余Component

@Component(modules = {MainModule.class}, dependencies = AppConponent.class)
public interface MainConponent {
    void inject(MainActivity activity);
}
复制代码

注意这里有坑,一下会讲解


(3) @Named注解使用

至关于有个表示,虽然你们都是同一个对象,可是实例化对象不一样就不如

A a1 = new A();
A a2 = new A();

// a1  a2 能同样嘛
复制代码

Module中 使用@Named注解

@Module
public class MainModule {

    private MainActivity activity;

    public MainModule(MainActivity activity) {
        this.activity = activity;
    }

    @Named("dev")
    @Provides
    MainApi provideMainApiDev(MainChildApi mainChildApi, String url) {
        return new MainApi(mainChildApi, activity,"dev");
    }

    @Named("release")
    @Provides
    MainApi provideMainApiRelease(MainChildApi mainChildApi, String url) {
        return new MainApi(mainChildApi, activity,"release");
    }

}
复制代码

在Activity/Fragment中使用

public class MainActivity extends AppCompatActivity {

    @Named("dev")
    @Inject
    MainApi apiDev;

    @Named("release")
    @Inject
    MainApi apiRelease;

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

        DaggerMainComponent.builder()
                .mainModule(new MainModule(this))
                .mainChildModule(new MainChildModule())
                .build()
                .inject(this);
        apiDev.eat();
        apiRelease.eat();
        Log.i("TAG","apiDev--->" + apiDev);
        Log.i("TAG","apiRelease--->" + apiRelease);
    }

}
复制代码

打印Log

07-14 01:46:01.170 2006-2006/? I/TAG: apiDev--->com.allen.rxjava.MainApi@477928f
07-14 01:46:01.170 2006-2006/? I/TAG: apiRelease--->com.allen.rxjava.MainApi@f2b291c
复制代码

(4) @Singleton注解

单例模式,是否是超级方便,你想然哪一个对象单例化,直接在他的Provider上添加@Singleton 就好了

例如

@Singleton
    @Provides
    A providerA(B b) {
        return new A(b);
    }
复制代码

注意: 第一个坑!!! 若是 moudule所依赖的Comonent 中有被单例的对象,那么Conponnent也必须是单例的

@Singleton
@Component(modules = {MainModule.class})
public interface MainConponent {
}
复制代码

而后 在Activity中使用,直接打印a1 a2 的地址,

@Inject
    A a2;
    @Inject
    A a1;
复制代码

能够看到Log

12-30 01:32:58.420 3987-3987/com.allens.daggerdemo E/TAG: A1---->com.allens.daggerdemo.Bean.A@11fa1ba
12-30 01:32:58.420 3987-3987/com.allens.daggerdemo E/TAG: A1---->com.allens.daggerdemo.Bean.A@11fa1ba
复制代码

不相信的小伙伴能够吧@Singleton去掉试试

如今咱们完成了单例,而后作了一个事情,就是点击某个按钮,跳转到一个新的Activiry,两边都引用一样一个A 对象,打印A 的地址,

说一下,一个Conponent 能够被对个Activity/Fragment 引用,如

@Singleton
@Component(modules = {MainModule.class})
public interface MainConponent {
    void inject(MainActivity activity);
    void inject(TestAct activity);
}
复制代码

上面与两个Activity, MainActivity 和 TestAct ,都引用相同的对象,答应地址看看

12-30 00:48:17.477 2788-2788/com.allens.daggerdemo E/TAG: A1---->com.allens.daggerdemo.Bean.A@11fa1ba
12-30 00:48:17.517 2788-2788/com.allens.daggerdemo E/TAG: A2---->com.allens.daggerdemo.Bean.A@4f81861
复制代码

居然不一样,说好的单例呢

注意: 第二个坑,单例对象只能在同一个Activity中有效。不一样的Activity 持有的对象不一样

那有人就要问了,没什么办法么,我就想全局只要一个实例化对象啊? 办法确定是有的,


(5) 自定义Scoped

/**
 * @做者 :  Android-SuperMan
 * @建立日期 :2017/7/14 下午3:04
 * @方法做用:
 * 参考Singleton 的写法
 * Scope 标注是Scope
 * Documented 标记在文档
 * @Retention(RUNTIME) 运行时级别
 */
@Scope
@Documented
@Retention(RUNTIME)
public @interface ActivityScoped {
}
复制代码

首先想一下,什么样的对象,可以作到全局单例,生命周期确定和APP 绑定嘛,这里我作演示,一个AppAip 咱们要对这个对象,全局单例,因此二话不说,先给Application 来个全家桶,

Module

@Module
public class AppModule {

    @Singleton
    @Provides
    AppApi providerAppApi() {
        return new AppApi();
    }
}
复制代码

Component

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
    AppApi getAppApi();
}
复制代码

Application

public class MyApp extends Application {

    private AppConponent appComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        appComponent = DaggerAppConpoment.create();
    }

    public AppConponent getAppComponent() {
        return appConponent;
    }
}
复制代码

最后是如何使用

首先,这个是个桥梁,依赖方式,上文已经说过了

@ActivityScoped
@Component(modules = {MainModule.class}, dependencies = AppConponent.class)
public interface MainComponent {
    void inject(MainActivity activity);
    void inject(TestAct activity);
}
复制代码

细心的小伙伴可能已经发现了,只有在上面一个MainComponent添加了一个@ActivityScoped,这里说明一下,@Singleton是Application 的单例

注意,第三个坑,子类component 依赖父类的component ,子类component的Scoped 要小于父类的Scoped,Singleton的级别是Application

因此,咱们这里的@Singleton 级别大于咱们自定义的@ActivityScoped,同时,对应module 所依赖的component ,也要放上相应的Scope

好吧,上面的例子,打印Log.

12-30 02:16:30.899 4717-4717/? E/TAG: A1---->com.allens.daggerdemo.Bean.AppApi@70bfc2
12-30 02:16:31.009 4717-4717/? E/TAG: A2---->com.allens.daggerdemo.Bean.AppApi@70bfc2
复制代码

同样啦

爬坑指南(极度重要)

  1. Provide 若是是单例模式 对应的Compnent 也要是单例模式
  2. inject(Activity act) 不能放父类
  3. 即便使用了单例模式,在不一样的Activity 对象仍是不同的
  4. 依赖component, component之间的Scoped 不能相同
  5. 子类component 依赖父类的component ,子类component的Scoped 要小于父类的Scoped,Singleton的级别是Application
  6. 多个Moudle 之间不能提供相同的对象实例
  7. Moudle 中使用了自定义的Scoped 那么对应的Compnent 使用一样的Scoped

(6)Subcomponent

这个是系统提供的一个Component,当使用Subcomponent,那么默认会依赖Component

例如

@Subcomponent(modules = TestSubModule.class)
public interface TestSubComponent {
    void inject(MainActivity activity);
}
复制代码
@Component(modules = {MainModule.class})
public interface MainConponent {
    TestSubComponent add(TestSubModule module);
}
复制代码

TestSubComponent中 我void inject(MainActivity activity);,即是这个桥梁,我是要注入到MainActivity,可是dagger 并不会给我生成一个Dagger开头的DaggerTestSubComponent 这个类,若是我想使用TestSubModule.class里面提供的对象,依然仍是使用DaggerMainConponent例如

DaggerMainConponent
                .builder()
                .mainModule(new MainModule())
                .build()
                .add(new TestSubModule())
                .inject(this);
复制代码

能够看到这里有一个add的方法,真是我在MainConponent添加的TestSubComponent add(TestSubModule module);


(7)lazy 和 Provider

public class Main3Activity extends AppCompatActivity {

    @PresentForContext
    @Inject
    Lazy<Present>     lazy;
    @PresentForName
    @Inject
    Provider<Present> provider;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);

        AppComponent appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
        ActivityComponent activityComponent = DaggerActivityComponent.builder()
                .appComponent(appComponent)
                .activityModule(new ActivityModule())
                .build();

        activityComponent.injectActivity(this);
        Present present = lazy.get();
        Present present1 = provider.get();
    }
}
复制代码

其中Lazy(懒加载)的做用比如component初始化了一个present对象,而后放到一个池子里,须要的时候就get它,因此你每次get的时候拿到的对象都是同一个;而且当你第一次去get时,它才会去初始化这个实例.

procider(强制加载)的做用: 1:同上当你第一次去get时,它才会去初始化这个实例 2:后面当你去get这个实例时,是否为同一个,取决于他Module里实现的方式


四: MVP + Dagger2

这是如今主流的设计架构

MVP,这个我会在后面的文章介绍,这里不作太多解释

当你了解MVP 的时候,你就知道,全部的业务逻辑全在Presenter, 换句话, presenter 持有的对象,控制着你程序的所有逻辑,这在dagger 中,讲白了 咱们只要将全部的presetner 对象控制就能够了

下面附上目录结构,固然仅仅做为参考。dagger 强大的用法仍是须要各位本身去体会,下面的项目是我刚刚学会dagger 时候 写的一个项目

能够看到,我是将全部的activity 或者 fragment 所有添加在同一个Component中,固然如今的话不推荐,好比Utils 你能够专门作一个Component,

首先放上个人Module,公司项目,不少东西没敢放上来,体谅,能够看到我这里提供了一个SplashPresenter,也就是启动页的Presneter,业务逻辑

@Module
public class ApiModule {

    public ApiModule() {

    }

    @Provides
    @Singleton
    Handler provideHandler() {
        return new Handler();
    }

    @Provides
    @Singleton
    SQLiteDatabase provideSQLiteDatabase() {
        return new DataBaseHelper(MyApp.context, Config.SqlName, null, Config.SqlVersion).getWritableDatabase();
    }

    /**
     * @ User :  Android-SuperMan
     * @ 建立日期 :  2017/7/13 下午3:24
     * @模块做用 :
     * <p>
     * ====================================================================================================================================
     * ====================================================================================================================================
     */
    private SplashPresenter splashPresenter;

    public ApiModule(SplashAct splashAct) {
        splashPresenter = new SplashPresenter(splashAct, new SplashModel());
    }

    @Provides
    @Singleton
    SplashPresenter provideSplashPresenter() {
        return splashPresenter;
    }

    .....
}
复制代码

当我使用的时候,只须要注入便可,以下代码

public class SplashAct extends BaseActivity implements SplashContract.View {

    @Inject
    SplashPresenter presenter;

    @Inject
    Handler handler;

    @Inject
    ApiService apiService;

    @Override
    protected void onCreate() {
        setContentView(R.layout.activity_splash);
    }

    @Override
    protected void initInject() {
        DaggerApiComponent.builder()
                .apiModule(new ApiModule(this))
                .build()
                .inject(this);
    }

    @Override
    protected void initListener() {
        presenter.getWordsInfo(true, apiService);
    }

    @Override
    public void gotoLogInAct() {
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                startActivity(new Intent(SplashAct.this, LogInAct.class));
                finish();
            }
        }, 1500);
    }
}
复制代码

————————分割线————————

若是你看到了这里,以为文章写得不错就给个呗?若是你以为那里值得改进的,请给我留言。必定会认真查询,修正不足。谢谢。

为何某些人会一直比你优秀,是由于他自己就很优秀还一直在持续努力变得更优秀,而你是否是还在知足于现状心里在窃喜!但愿读到这的您能点个小赞关注下我,之后还会更新技术干货,谢谢您的支持!

转发分享+关注,天天获取更多资料

Android架构师之路很漫长,一块儿共勉吧!

相关文章
相关标签/搜索