Dragger2使用

1. 添加依赖

1. 在Project的 build.gradle文件添加

    classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    classpath 'me.tatarka:gradle-retrolambda:3.2.4'

2. 在App下的build.gradle添加

头部添加
apply plugin: 'com.neenbedankt.android-apt'
apply plugin: 'me.tatarka.retrolambda'


android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}


dependencies {
    apt 'com.google.dagger:dagger-compiler:2.2'
    compile 'com.google.dagger:dagger:2.2'
    provided 'org.glassfish:javax.annotation:10.0-b28'
}

3. 如果下载不下来可以在gradle.properties文件中设置代理 我这里是设置的shadowsocks的代理

systemProp.http.proxyHost=127.0.0.1
systemProp.http.proxyPort=1080

2. 基础概念

依赖注入:就是目标类(目标类需要进行依赖初始化的类,下面都会用目标类一词来指代)中
所依赖的其他的类的初始化过程,不是通过手动编码的方式创建,而是通过技术手段可以把
其他的类的已经初始化好的实例自动注入到目标类中。

依赖注入的过程:

步骤1:查找Module中是否存在创建该类的方法。

步骤2:若存在创建类方法,查看该方法是否存在参数

    步骤2.1:若存在参数,则按从步骤1开始依次初始化每个参数
    步骤2.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束

步骤3:若不存在创建类方法,则查找Inject注解的构造函数,看构造函数是否存在参数

    步骤3.1:若存在参数,则从步骤1开始依次初始化每个参数
    步骤3.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
  • Inject用来标注依赖和被依赖的构造函数

  • Provides提供依赖的方法上添加的注解,provide方法需要包含在Module中

  • Module专门提供依赖,类似工厂模式,包含Provides方法

  • Component它是一个桥梁,一端是目标类,另一端是目标类所依赖类的实例,它也是注入器(Injector)负责把目标类所依赖类的实例注入到目标类中,同时它也管理Module。(先从Module中找依赖,再从Inject找构造函数)

  • Module和Provides是为解决第三方类库而生的,Module是一个简单工厂模式,Module可以包含创建类实例的方法,这些方法用Provides来标注

  • Scope自定义注解,用于标示作用域,命名随意,对应即可,其中@Singleton是一个系统的模式实现.(管理Module与Component的匹配)

  • Qualifier自定义注解,用于解决一个实例可以被多种方式构建的依赖迷失问题

  • Component有三种组织关系,分为依赖,包含和继承,用于解决依赖复用与共享问题

  • 依赖方式
    一个Component是依赖于一个或多个Component,Component中的dependencies属性就是依赖方式的具体实现

  • 包含方式
    一个Component是包含一个或多个Component的,被包含的Component还可以继续包含其他的Component。这种方式特别像Activity与Fragment的关系。SubComponent就是包含方式的具体实现。

  • 继承方式
    官网没有提到该方式,具体没有提到的原因我觉得应该是,该方式不是解决类实例共享的问题,而是从更好的管理、维护Component的角度,把一些Component共有的方法抽象到一个父类中,然后子Component继承。

Scope用法

  • 更好的管理Component之间的组织方式,不管是依赖方式还是包含方式,都有必要用自定义的Scope注解标注这些Component,这些注解最好不要一样了,不一样是为了能更好的体现出Component之间的组织方式。还有编译器检查有依赖关系或包含关系的Component,若发现有Component没有用自定义Scope注解标注,则会报错。

  • 更好的管理Component与Module之间的匹配关系,编译器会检查 Component管理的Modules,若发现标注Component的自定义Scope注解与Modules中的标注创建类实例方法的注解不一样,就会报错。

  • 可读性提高,如用Singleton标注全局类,这样让程序猿立马就能明白这类是全局单例类。

3. @Inject 和 @Component使用

    新建一个类
    public class Person {

        private String desc;
        @Inject
        public Person(){
            this.desc = "生命不息奋斗不止";
        }

        public Person(String desc) {
            this.desc = desc;
        }

        public String getDesc() {
            return desc;
        }
    }


    MainActivity中使用这个类

    public class MainActivity extends AppCompatActivity {

        //添加@Inject注解,表示这个mPoetry是需要注入的
        @Inject
        Person person;

        private TextView mTextView;

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

            initView();
        }

        private void initView() {
            mTextView = (TextView) findViewById(R.id.id_tv);
            mTextView.setText(person.getDesc());
        }
    }


    需要一个连接器Component,让上面这两个类产生联系

    //用@Component表示这个接口是一个连接器,能用@Component注解的只能是interface或者抽象类
    @Component
    public interface MainComponent {

        /**
         * 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
         * (被标记为@Inject的属性)
         * 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
         * 用inject即可。
         */
        void inject(MainActivity activity);
    }

    修改MainActivity

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

        // 使用Dagger2生成的类 生成组件进行构造,并注入
        DaggerMainComponent.builder().build().inject(this);

        initView();
    }

4. @Module 和 @Provides 和 @Component使用

    Module和Provides是为解决第三方类库而生的,Module是一个简单工厂模式,Module可以包含创建类实例的方法,这些方法用Provides来标注

    @Module
    public class MainModule {

        @Provides
        public Gson provideGson(){
            return new Gson();
        }

    }

    添加完这个类后,我们要与之前写的类产生关联

    修改MainCompontent

    //这里表示Component会从MainModule类中拿那些用@Provides注解的方法来生成需要注入的实例
    @Component(modules = MainModule.class)
    public interface MainComponent {

        /**
         * 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
         * (被标记为@Inject的属性)
         * 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
         * 用inject即可。
         */
        void inject(MainActivity activity);
    }

    这里多了一个依赖,依赖MainModule类中的方法生成Gson实例,我们在MainActivity里注入Gson实例:

    public class MainActivity extends AppCompatActivity {

        //添加@Inject注解,表示这个person是需要注入的
        @Inject
        Person person;

        @Inject
        Gson mGson;

        private TextView mTextView;

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

            // 使用Dagger2生成的类 生成组件进行构造,并注入
            DaggerMainComponent.builder().build().inject(this);

            initView();
        }

        private void initView() {
            mTextView = (TextView) findViewById(R.id.id_tv);
            String json = mGson.toJson(person);
            mTextView.setText(json);
        }
    }

5. @Component依赖多个@Module

    创建一个新的PeronModule

    @Module
    public class PeronModule {

        // 这个方法需要一个String参数,在Dagger2注入中,这些参数也是注入形式的,也就是
        // 要有其他方提供参数desc的生成,不然会造成编译出错
        @Provides
        public Person providePerson(String desc){
            return new Person(desc);
        }

        // 这里提供了一个生成String的方法,在这个Module里生成Person实例时,会查找到这里
        // 可以为上面提供String类型的参数
        @Provides
        public String getDesc(){
            return "代码改变世界 双手成就未来";
        }
    }

    修改MainComponent依赖:

    //这里表示Component会从MainModule和PeronModule类中拿那些用@Provides注解的方法来生成需要注入的实例
    @Component(modules = {MainModule.class,PeronModule.class})
    public interface MainComponent {

        /**
         * 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
         * (被标记为@Inject的属性)
         * 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
         * 用inject即可。
         */
        void inject(MainActivity activity);

    }

    Dagger2是先从@Privodes查找类实例,如果找到了就用@Module提供的方法来创建类实例,
    如果没有就从构造函数里用@Inject注解的生成类实例,如果二者都没有,则报错,
    简而言之,就是@Module的优先级高于@Inject

6. @Scope

    我们创建多一个Activity,这个Activity也注入了Person和Gson对象:

    public class SecondActivity extends AppCompatActivity {

        @Inject
        Person p;

        @Inject
        Gson gson;

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

            MainComponent.getInstance().inject(this);

            TextView id_tv = (TextView) findViewById(R.id.id_tv);
            String json = gson.toJson(p);
            id_tv.setText(json + " \nperson="+p);
        }
    }

    把MainComponent改成抽象类的形式,并添加返回MainComponent单例的方法

    @Component(modules = {MainModule.class,PoetryModule.class})
    public abstract class MainComponent {

        /**
         * 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
         * (被标记为@Inject的属性)
         * 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
         * 用inject即可。
         */
        abstract void inject(MainActivity activity);

        abstract void inject(SecondActivity activity);

        private static final class SingleHolder{
            private final static MainComponent mainComponent = DaggerMainComponent.builder().build();
        }

        public static MainComponent getInstance(){
            return SingleHolder.mainComponent;
        }
    }

    调用同一个MainComponent实例多次注入的时候还是会重新生成Person实例,
    有时候我们需要只希望生成一个共用实例的时候应该怎么办呢,这里我们就需要用到
    Dagger2的@Scope属性了,Scope是作用域的意思,我们先自定义一个@Scope注解:

    @Scope
    @Retention(RetentionPolicy.RUNTIME)
    public @interface PersonScore {

    }

    同时在Module与Component加上这个自定义Scope

    @PersonScore
    @Component(modules = {MainModule.class,PeronModule.class})
    public abstract class MainComponent {

        /**
         * 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
         * (被标记为@Inject的属性)
         * 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
         * 用inject即可。
         */
        abstract void inject(MainActivity activity);

        abstract void inject(OtherActivity activity);

        private static final class SingleHolder{
            private final static MainComponent mainComponent = DaggerMainComponent.builder().build();
        }

        public static MainComponent getInstance(){
            return SingleHolder.mainComponent;
        }
    }

    @Module
    public class PeronModule {

        @PersonScore
        @Provides
        public Person providePerson(String desc){
            return new Person(desc);
        }

        @Provides
        public String getDesc(){
            return "代码改变世界 双手成就未来";
        }
    }

    通过实现自定义@Scope注解,标记当前生成对象的使用范围,标识一个类型的注射器只实例化一次,
    在同一个作用域内,只会生成一个实例,然后在此作用域内共用一个实例,
    @Singleton其实就是@Scope的一个默认实现而已

7. 组织Component

1. 依赖方式

    一个Component可以依赖一个或多个Component,并拿到被依赖Component暴露出来的实例,
    Component的dependencies属性就是确定依赖关系的实现。如我们在ApplicationModule提供了
    一个全局的Gson对象,我们想要提供给其他Component时,要在ApplicationComponent显式的提供一个接口:

    创建一个新的Module

    @Module
    public class ApplicationModule {

        @Singleton
        @Provides
        public Gson provideGson(){
            return new Gson();
        }
    }

    //创建新的Component

    @Singleton
    @Component(modules = ApplicationModule.class)
    public interface ApplicationComponent {

        Gson getGson();  //获取Gson对象
    }

    在自定义的MyApplication中初始化它,更改MainComponent:

    public class MyApplication extends Application {

        private static MyApplication application;
        private static ApplicationComponent applicationComponent;
        @Override
        public void onCreate() {
            super.onCreate();

            application = this;

            applicationComponent = DaggerApplicationComponent.create();
        }

        public static MyApplication getApplication() {
            return application;
        }

        public static ApplicationComponent getApplicationComponent() {
            return applicationComponent;
        }

    }

    //这里表示Component会从MainModule类中拿那些用@Provides注解的方法来生成需要注入的实例
    @PersonScore
    @Component(dependencies = ApplicationComponent.class, modules = {MainModule.class,PersonModule.class})
    public abstract class MainComponent {

        /**
         * 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
         * (被标记为@Inject的属性)
         * 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
         * 用inject即可。
         */
        public abstract void inject(MainActivity activity);

        public abstract void inject(OtherActivity activity);

        private static final class SingleHolder{
            private final static MainComponent mainComponent = DaggerMainComponent.builder()
                .applicationComponent(MyApplication.getApplicationComponent()).build();
        }

        public static MainComponent getInstance(){

            return SingleHolder.mainComponent;
        }
    }

    这样就达到了MainComponent依赖ApplicationComponent。并且这里需要注意的是,
    MainComponent的作用域不能和ApplicationComponent的作用域一样,否则会报错,
    一般来讲,我们应该对每个Component都定义不同的作用域。

2. 包含方式(从属方式)@SubComponent

    如果我们需要父组件全部的提供对象,这时我们可以用包含方式而不是用依赖方式,
    相比于依赖方式,包含方式不需要父组件显式显露对象,就可以拿到父组件全部对象。
    SubComponent只需要在父Component接口中声明就可以了

    定义一个新的Scope
    @Scope
    @Retention(RetentionPolicy.RUNTIME)
    public @interface SubScope {

    }

    定义一个新的Module    

    @Module
    public class SubModule {

        @SubScope
        @Provides
        public Person getPerson(){
            return new Person("生活不易,且行且珍惜吧");
        }

    }

    定义一个新的Component

    @SubScope
    @Subcomponent(modules = SubModule.class)
    public interface SubComponent {
        void inject(ThreeActivity activity);
    }


    在ApplicationComponent中添加一个创建SubComponent的方法

    @Singleton
    @Component(modules = ApplicationModule.class)
    public interface ApplicationComponent {
        Gson getGson();

        SubComponent createComponent(SubModule module);
    }


    自定义的MyApplication中添加一个方法

    private SubComponent subComponent;
    public SubComponent getSubComponent(){
        if (subComponent == null){
            subComponent = applicationComponent.createComponent(new SubModule());
        }
        return subComponent;
    }


    新创捷一个ThreeActivity

    public class ThreeActivity extends AppCompatActivity {

        @Inject
        Person p;
        @Inject
        Gson gson;

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

            MyApplication.getApplication().getSubComponent().inject(this);

            findViewById(R.id.id_btn).setVisibility(View.INVISIBLE);
            TextView id_tv = (TextView) findViewById(R.id.id_tv);
            String text = p.getDesc()+" \n,person:"+p+"\n"+( gson == null ? "Gson没被注入" : "Gson已经被注入");
            id_tv.setText(text);
        }
    }

8. @Qualifier

    假如在上面的ThreeAtivity里面我们想要注入两个不同的Person(指desc不一样)实例,
    我们可以在SubModule下添加多一个生成Person的方法:

    自定义@Qualifier限定符

    @Qualifier
    @Documented
    public @interface PersonQualifier {
        String value() default "";
    }


    @Module
    public class SubModule {

        @PersonQualifier("a")
        @SubScope
        @Provides
        public Person getPerson(){
            return new Person("生活不易,且行且珍惜吧");
        }

        @PersonQualifier("b")
        @SubScope
        @Provides
        public Person getPerson2(){
            return new Person("呵呵呵呵呵呵呵");
        }
    }

    修改Activity代码

    public class ThreeActivity extends AppCompatActivity {

        @PersonQualifier("a")
        @Inject
        Person p;

        @PersonQualifier("b")
        @Inject
        Person p1;

        @Inject
        Gson gson;

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

            MyApplication.getApplication().getSubComponent().inject(this);

            findViewById(R.id.id_btn).setVisibility(View.INVISIBLE);
            TextView id_tv = (TextView) findViewById(R.id.id_tv);
            String text = p.getDesc()+" \n,person:"+p+"\n"+p1.getDesc()+" \n,person:"+p1+"\n"+( gson == null ? "Gson没被注入" : "Gson已经被注入");
            id_tv.setText(text);
        }
    }

    Dagger2已经默认帮我们实现了一个@Named和自定义的PersonQualifier其实是一样的。

9.联系方式

qq:1509815887@qq.com 
email : zlc921022@163.com 
phone : 18684732678

10. 参考

Dagger2 使用详解
Android:dagger2让你爱不释手-基础依赖注入框架篇
Android:dagger2让你爱不释手-重点概念讲解、融合篇
Android:dagger2让你爱不释手-终结篇

猜你喜欢

转载自blog.csdn.net/rjgcszlc/article/details/78364689