Dagger2 使用详解

作者: zpayh | 来源:发表于2016-07-07 00:37 被阅读11832次

    标签: Android Dagger2


    更新

    伴随着 Android Gradle 插件 2.2 版本的发布,近期 android-apt 作者在官网发表声明证实了后续将不会继续维护 android-apt,并推荐大家使用 Android 官方插件提供的相同能力。也就是说,大约三年前推出的 android-apt 即将告别开发者,退出历史舞台,Android Gradle 插件提供了名为 annotationProcessor 的功能来完全代替 android-apt
    所以新的配置信息可以更加简单,在Project的 build.gradle文件添加以下内容:

    buildscript {
        
        dependencies {
            classpath 'me.tatarka:gradle-retrolambda:3.5.0'//支持lambda表达式,在Dagger2中可以不用添加
        }
    }
    

    然后在Module下的build.gradle添加以下内容:

    apply plugin: 'me.tatarka.retrolambda'
    
    android {
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    }
    
    dependencies {
        annotationProcessor 'com.google.dagger:dagger-compiler:2.4'
        compile 'com.google.dagger:dagger:2.4'
        provided 'org.glassfish:javax.annotation:10.0-b28'
    }
    

    前言

    Dagger2 是一款使用在Java和Android上的依赖注入的一个类库。

    配置信息

    使用Android Studio 创建一个新的项目,在Project的 build.gradle文件添加以下内容:

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

    并在Module下的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.4'
        compile 'com.google.dagger:dagger:2.4'
        provided 'org.glassfish:javax.annotation:10.0-b28'
    }
    

    这样就基本完全了Dagger2的配置环境(顺便也配置了支持lambda表达式)。

    Dagger2基本使用

    我们先简单地创建一个类:

    public class Poetry {
        private String mPemo;
    
        // 用Inject标记构造函数,表示用它来注入到目标对象中去
        @Inject
        public Poetry() {
            mPemo = "生活就像海洋";
        }
    
        public String getPemo() {
            return mPemo;
        }
    }
    

    然后我们在MainActivity中使用这个类:

    public class MainActivity extends AppCompatActivity {
    
        //添加@Inject注解,表示这个mPoetry是需要注入的
        @Inject
        Poetry mPoetry;
    
        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.tv_poetry);
            mTextView.setText(mPoetry.getPoems());
        }
    }
    

    但是这样直接运行是会出错的,此时这样子在MainActivity中的mPoetry对象是无法被注入的,因为MainActivity不知道去哪里找到它的实例去注入生成,这时我们需要一个连接器Component,让上面这两个类产生联系:

    //用@Component表示这个接口是一个连接器,能用@Component注解的只
    //能是interface或者抽象类
    @Component
    public interface MainComponent {
    
        /**
         * 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
         * (被标记为@Inject的属性)
         * 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
         * 用inject即可。
         */
        void inject(MainActivity activity);
    }
    

    先运行一遍,AS会生成一些类,再修改一下MainActivity:

    public class MainActivity extends AppCompatActivity {
    
        //添加@Inject注解,表示这个mPoetry是需要注入的
        @Inject
        Poetry mPoetry;
    
        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.tv_poetry);
            mTextView.setText(mPoetry.getPoems());
        }
    }
    

    运行,如下

    运行结果运行结果
    上面MainActivity中的Poetry实例并不直接由MainActivity类创建,而是由MainActivityComponent类注入生成实例。以上就是一个简单的Dagger2示例。

    @Module

    有时候我们并不能直接在构造函数里面添加@Inject注解,或者类中存在多个构造函数时,@Inject也只能注解其中一个构造函数,不能注解多个构造函数,这里是会产生歧义性,因为Dagger2无法确认调用哪一个构造函数来生成例的实例对象。另外一种情况是我们在项目中引用第三方类库时,也是无法直接在类构造函数中添加@Inject注解的,所以我们需要用到@Module注解了。
    @Module是用来生产实例来注入对象的,它类似一个工厂,集中创建要注入的类的对象实例。下面我们引用一下Gson库来看看@Module是怎么使用的,创建MainModule类:

    /*
    @Module注解表示这个类提供生成一些实例用于注入
     */
    @Module
    public class MainModule {
    
        /**
         * @Provides 注解表示这个方法是用来创建某个实例对象的,这里我们创建返回Gson对象
         * 方法名随便,一般用provideXXX结构
         * @return 返回注入对象
         */
        @Provides
        public Gson provideGson(){
            return new Gson();
        }
    }
    

    添加完这个类后,我们要与之前写的类产生关联,不然谁知道你这里提供了生成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注解,表示这个mPoetry是需要注入的
        @Inject
        Poetry mPoetry;
    
        @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.tv_poetry);
            String json = mGson.toJson(mPoetry);
            mTextView.setText(json);
        }
    }
    

    运行,结果如下:

    运行结果运行结果
    Component可以依赖多个Module对象,以上的构造方法与生成方法都是无参生成实例的,如果我们带参数应该怎么做了?我们创建多一个PoetryModule用于提供Poetry实例:
    @Module
    public class PoetryModule {
    
        // 这个方法需要一个String参数,在Dagger2注入中,这些参数也是注入形式的,也就是
        // 要有其他对方提供参数poems的生成,不然会造成编译出错
        @Provides
        public Poetry providePoetry(String poems){
            return new Poetry(poems);
        }
        
        // 这里提供了一个生成String的方法,在这个Module里生成Poetry实例时,会查找到这里
        // 可以为上面提供String类型的参数
        @Provides
        public String providePoems(){
            return "只有意志坚强的人,才能到达彼岸";
        }
    }
    

    修改MainComponent依赖:

    //这里表示Component会从MainModule类中拿那些用@Provides注解的方法来生成需要注入的实例
    @Component(modules = {MainModule.class,PoetryModule.class})
    public interface MainComponent {
    
        /**
         * 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
         * (被标记为@Inject的属性)
         * 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
         * 用inject即可。
         */
        void inject(MainActivity activity);
    }
    

    运行,就可以看到不同的诗词了:

    运行结果运行结果
    细心的同学就会发现了,我们提供了两个可以生成Poetry实例的方法,一个是在Poetry类的构造函数时候用@Inject提供的实例创建方法,一个是在PoetryModule中的@Privodes注解的providePoetry方法,而在上面的运行结果中我们发现是调用了PoetryModule提供的方法,这里就要说明一下优先级的问题,在上面这种既在构造函数中用@Inject提供注入来源,也在@Module中用@Privodes注解提供注入来源的,Dagger2是先从@Privodes查找类实例,如果找到了就用@Module提供的方法来创建类实例,如果没有就从构造函数里用@Inject注解的生成类实例,如果二者都没有,则报错,简而言之,就是@Module的优先级高于@Inject
    另外这里还要说明一点,在providePoetry(String)方法中,String这个参数也是要注入提供的,必须也要有在同一个连接器里面有提供,其中在构建类实例的时候,会按照以下顺序执行:
    1. 从Module中查找类实例创建方法
    2. Module中存在创建方法,则看此创建方法有没有参数
      1. 如果有参数,这些参数也是由Component提供的,返回步骤1逐一生成参数类实例,最后再生成最终类实例
      2. 如果无参数,则直接由这个方法生成最终类实例
    3. Module中没有创建方法,则从构造函数里面找那个用@Inject注解的构造函数
      1. 如果该构造函数有参数,则也是返回到步骤1逐一生成参数类实例,最后调用该构造函数生成类实例
      2. 如果该构造函数无参数,则直接调用该构造函数生成类实例

    以上就是一次注入生成类实例的生成步骤。

    @Scope

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

    public class OtherActivity extends AppCompatActivity {
    
        //添加@Inject注解,表示这个mPoetry是需要注入的
        @Inject
        Poetry mPoetry;
    
        @Inject
        Gson mGson;
    
        private TextView mTextView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_other);
    
            MainComponent.getInstance()
                    .inject(this);
    
            initView();
        }
    
        private void initView() {
            mTextView = (TextView) findViewById(R.id.tv_poetry);
            String json = mGson.toJson(mPoetry);
            String text = json + ",mPoetry:"+mPoetry;
            mTextView.setText(text);
        }
    }
    

    我们顺便也把MainComponent改成抽象类的形式,并添加返回MainComponent单例的方法,对应添加MainActivity跳转到OtherActivity的方法.

    @Component(modules = {MainModule.class,PoetryModule.class})
    public abstract class MainComponent {
    
        /**
         * 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
         * (被标记为@Inject的属性)
         * 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
         * 用inject即可。
         */
        abstract void inject(MainActivity activity);
    
        abstract void inject(OtherActivity activity);
    
        private static MainComponent sComponent;
        public static MainComponent getInstance(){
            if (sComponent == null){
                sComponent = DaggerMainComponent.builder().build();
            }
            return sComponent;
        }
    }
    
    public class MainActivity extends AppCompatActivity {
    
        //添加@Inject注解,表示这个mPoetry是需要注入的
        @Inject
        Poetry mPoetry;
    
        @Inject
        Gson mGson;
    
        private TextView mTextView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            // 使用Dagger2生成的类 生成组件进行构造,并注入
            MainComponent.getInstance()
                    .inject(this);
    
            initView();
        }
    
        private void initView() {
            mTextView = (TextView) findViewById(R.id.tv_poetry);
            String json = mGson.toJson(mPoetry);
            String text = json + ",mPoetry:"+mPoetry;
            mTextView.setText(text);
    
            findViewById(R.id.open).setOnClickListener(view ->
                    startActivity(new Intent(this,OtherActivity.class)));
        }
    }
    

    运行结果如下:

    运行结果运行结果 运行结果运行结果
    可以看到,调用同一个MainComponent实例多次注入的时候每次都重新生成Poetry实例,有时候我们需要只希望生成一个共用实例的时候应该怎么办呢,这里我们就需要用到Dagger2的@Scope属性了,Scope是作用域的意思,我们先自定义一个@Scope注解:
    @Scope
    @Retention(RetentionPolicy.RUNTIME)
    public @interface PoetryScope {
    }
    

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

    @PoetryScope
    @Component(modules = {MainModule.class,PoetryModule.class})
    public abstract class MainComponent {
        /**
         * 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
         * (被标记为@Inject的属性)
         * 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
         * 用inject即可。
         */
        abstract void inject(MainActivity activity);
    
        abstract void inject(OtherActivity activity);
    
        private static MainComponent sComponent;
        public static MainComponent getInstance(){
            if (sComponent == null){
                sComponent = DaggerMainComponent.builder().build();
            }
            return sComponent;
        }
    }
    
    @Module
    public class PoetryModule {
    
        // 这个方法需要一个String参数,在Dagger2注入中,这些参数也是注入形式的,也就是
        // 要有其他对方提供参数poems的生成,不然会造成编译出错
        @PoetryScope
        @Provides
        public Poetry providePoetry(String poems){
            return new Poetry(poems);
        }
    
        // 这里提供了一个生成String的方法,在这个Module里生成Poetry实例时,会查找到这里
        // 可以为上面提供String类型的参数
        @Provides
        public String providePoems(){
            return "只有意志坚强的人,才能到达彼岸";
        }
    }
    

    重新运行:


    运行结果运行结果 运行结果运行结果

    这时你会发现这两个Poetry实例是同一个实例来的,通过实现自定义@Scope注解,标记当前生成对象的使用范围,标识一个类型的注射器只实例化一次,在同一个作用域内,只会生成一个实例,然后在此作用域内共用一个实例。这样看起来很像单例模式,我们可以查看@Singleton其实就是@Scope的一个默认实现而已。当然,你得是同一个Component对象来生成,这点我们应该可以理解的吧。
    我们可以通过自定义Scope来组织Component的作用域,使得每个Component的作用域清晰明了,各施其职。

    组织Component

    我们在一个项目之中不可能只使用一个Component连接器来注入对象完成注入工作,一般除了一个全局的ApplicationComponent之外,还有一些作用域在Activity/Fragment的Component,Component之间存在依赖关系与从属关系。如果我们已经创建好了一个全局的ApplicationComponent,然后其它的Component刚好需要ApplicationComponent里面的一个全局属性,想要与ApplicationComponent共享同一个实例,这时就需要用到依赖关系了。

    依赖方式

    一个Component可以依赖一个或多个Component,并拿到被依赖Component暴露出来的实例,Component的dependencies属性就是确定依赖关系的实现。
    这里的有点像数学里面的交集方式,被依赖的Component主动暴露对象给二者共享,如我们在ApplicationModule提供了一个全局的Gson对象,我们想要提供给其他Component时,要在ApplicationComponent显式的提供一个接口:

    @Module
    public class ApplicationModule {
    
        /**
         * @Provides 注解表示这个方法是用来创建某个实例对象的,这里我们创建返回Gson对象
         * 方法名随便,一般用provideXXX结构
         * @return 返回注入对象
         */
        @Singleton
        @Provides
        public Gson provideGson(){
            return new Gson();
        }
    }
    
    @Singleton
    @Component(modules = ApplicationModule.class)
    public interface ApplicationComponent {
    
        Gson getGson();// 暴露Gson对象接口
    }
    

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

    public class MainApplication extends Application {
    
        private ApplicationComponent mApplicationComponent;
        private static MainApplication sApplication;
    
        public static MainApplication getInstance() {
            return sApplication;
        }
        @Override
        public void onCreate() {
            super.onCreate();
            sApplication = this;
    
            mApplicationComponent = DaggerApplicationComponent.builder().build();
        }
    
        public ApplicationComponent getApplicationComponent() {
            return mApplicationComponent;
        }
    }
    
    //这里表示Component会从MainModule类中拿那些用@Provides注解的方法来生成需要注入的实例
    @PoetryScope
    @Component(dependencies = ApplicationComponent.class, modules = {MainModule.class,PoetryModule.class})
    public abstract class MainComponent {
    
        /**
         * 需要用到这个连接器的对象,就是这个对象里面有需要注入的属性
         * (被标记为@Inject的属性)
         * 这里inject表示注入的意思,这个方法名可以随意更改,但建议就
         * 用inject即可。
         */
        abstract void inject(MainActivity activity);
    
        abstract void inject(OtherActivity activity);
    
        private static MainComponent sComponent;
        public static MainComponent getInstance(){
            if (sComponent == null){
                sComponent = DaggerMainComponent.builder()
                        .applicationComponent(MainApplication.getInstance()
                        .getApplicationComponent())
                        .build();
            }
            return sComponent;
        }
    }
    

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

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

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

    @Scope
    @Retention(RetentionPolicy.RUNTIME)
    public @interface AScope {
    }
    
    @Module
    public class AModule {
    
        @AScope
        @Provides
        public Poetry getPoetry(){
            return new Poetry("万物美好");
        }
    }
    
    @AScope
    @Subcomponent(modules = AModule.class)
    public interface AComponent {
        void inject(AActivity activity);
    }
    
    @Singleton
    @Component(modules = ApplicationModule.class)
    public interface ApplicationComponent {
    
        Gson getGson();// 暴露Gson对象接口
    
        //AComponent plus();
        AComponent plus(AModule module);//添加声明
    }
    
    
    public class MainApplication extends Application {
    
        private ApplicationComponent mApplicationComponent;
        private AComponent mAComponent;
        private static MainApplication sApplication;
    
        public static MainApplication getInstance() {
            return sApplication;
        }
        @Override
        public void onCreate() {
            super.onCreate();
            sApplication = this;
    
            mApplicationComponent = DaggerApplicationComponent.builder().build();
        }
    
        public ApplicationComponent getApplicationComponent() {
            return mApplicationComponent;
        }
    
        public AComponent getAComponent() {
            if (mAComponent == null){
                mAComponent = mApplicationComponent.plus(new AModule());
            }
            return mAComponent;
        }
    }
    
    public class AActivity extends AppCompatActivity {
        TextView mTextView;
    
        @Inject
        Gson mGson;
    
        @Inject
        Poetry mPoetry;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_a);
    
            MainApplication.getInstance()
                    .getAComponent()
                    .inject(this);
    
            mTextView = (TextView) findViewById(R.id.text);
            String text = mPoetry.getPoems()+",mPoetry:"+mPoetry+(mGson == null ? "Gson没被注入" : "Gson已经被注入");
            mTextView.setText(text);
        }
    }
    

    最后我们在OtherActivity中添加一个按钮跳转到AActivity,运行结果如下:


    运行结果运行结果

    @Qualifier

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

    @Module
    public class AModule {
    
        @AScope
        @Provides
        public Poetry getPoetry(){
            return new Poetry("万物美好");
        }
        
        @AScope
        @Provides
        public Poetry getOtherPoetry(){
            return new Poetry("我在中间");
        }
    }
    

    但是直接这样做Dagger2是无法区分调用哪个方法生成Poetry实例的,这个时候就需要自定义@Qualifier限定符来匹配注入方法了,添加一个自定义Qualifier并修AMoudule,AActivity:

    @Qualifier
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    public @interface PoetryQualifier {
        String value() default "";
    }
    
    @Module
    public class AModule {
    
        @PoetryQualifier("A")
        @AScope
        @Provides
        public Poetry getPoetry(){
            return new Poetry("万物美好");
        }
    
        @PoetryQualifier("B")
        @AScope
        @Provides
        public Poetry getOtherPoetry(){
            return new Poetry("我在中间");
        }
    }
    
    public class AActivity extends AppCompatActivity {
        TextView mTextView;
    
        @Inject
        Gson mGson;
    
        // 匹配Module中同样注解的方法
        @PoetryQualifier("A")
        @Inject
        Poetry mPoetry;
        
        // 匹配Module中同样注解的方法
        @PoetryQualifier("B")
        @Inject
        Poetry mPoetryB;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_a);
    
            MainApplication.getInstance()
                    .getAComponent()
                    .inject(this);
    
            mTextView = (TextView) findViewById(R.id.text);
            String text = mPoetry.getPoems()+",mPoetryA:"+mPoetry+
                    mPoetryB.getPoems()+",mPoetryB:"+mPoetryB+
                    (mGson == null ? "Gson没被注入" : "Gson已经被注入");
            mTextView.setText(text);
        }
    }
    

    重新编译运行:


    运行结果运行结果

    而Dagger2已经默认帮我们实现了一个@Named:

    @Qualifier
    @Documented
    @Retention(RUNTIME)
    public @interface Named {
    
        /** The name. */
        String value() default "";
    }
    

    跟我们自定义的PoetryQualifier其实是一样的。

    后记

    这篇是我参考了其他文章之后自己又重新总结一遍的,错误之处请帮忙指出,大家一起进步。除了以上常用到的注解之外,Dagger还提供了其他一些注解,如Set,Map类的注解,具体可以参考以下文章。

    参考

    Dagger2图文完全教程
    Google官方MVP+Dagger2架构详解【从零开始搭建android框架系列(6)】
    Android:dagger2让你爱不释手-基础依赖注入框架篇
    Android:dagger2让你爱不释手-重点概念讲解、融合篇
    Android:dagger2让你爱不释手-终结篇
    Android:Dagger2学习之由浅入深

    Demo地址

    https://github.com/EvilBT/-Dagger2Demo

    相关文章

      网友评论

      • Todo2:写的非常不错,赞
        组件化和插件化的开发里程总结
        https://www.jianshu.com/p/df2a6717009d
        zpayh:@Todo2 :blush:
      • 后来Memory:楼主你还,我一直有个疑问,构造带参数,那么我那个参数大部分情况下都是需要动态生成赋值的而不是提前写好的!在这种情况下又是如何注入得了 ?
        zpayh:构造带参数的,如果你用了@Inject 去注解构造函数的话,构造函数所需要的需要注入的,注入方式也是只要提供了@Provide注入的就可以了,它会自动去找它需要的参数
      • 后来Memory:我的构造参数肯定是动态传入的 那样的话又该怎么样注入了 楼主?
      • zx_7e60:写的很好,很多大神都是上来就讲mvp+dagger2,可能会影响新手对dagger2的理解
        zpayh:@zx_7e60 嗯嗯,但是实际上我已经删除掉一个的了,我应该有说到删了一个,实际代码中已经被删了的
        zx_7e60:@zpayh 对了,请问下在“组织Component”中,ApplicationComponent 应该是与 MainModule有冲突的的吧?因为他们注射的都是Gson类
        zpayh:@zx_7e60 谢谢支持
      • e117c11dfdc9:你好,那个在module里加有参数的Poetry, return new Poetry(name), 需要去原类里面加一个带String 参数的构造函数吗?
        zpayh:@Kyle_Zhou 不客气,这篇文章比较老了,如果有疑问,可以留言
        e117c11dfdc9:@zpayh 哦哦,我就说不然怎么new。。。多谢
        zpayh:@Kyle_Zhou 要的呀,不然怎么new Poetry(name)?
      • CarlWu_S:很好,感谢引路!
      • boboyuwu:又试了下
        annotationProcessor 'com.google.dagger:dagger-compiler:2.4'
        compile 'com.google.dagger:dagger:2.4'
        provided 'org.glassfish:javax.annotation:10.0-b28'

        这样就没效果运行也不生产dagger代码改成这样就可以不知道为啥
        // Dagger2
        compile 'com.google.dagger:dagger:2.11-rc2'
        annotationProcessor 'com.google.dagger:dagger-compiler:2.11-rc2'
        zpayh:@boboyuwu 用最新的就好了,我也是用2.11-rc2的,以前的有可能不太适用了
      • boboyuwu:然后在Module下的build.gradle添加以下内容:

        apply plugin: 'me.tatarka.retrolambda'

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

        dependencies {
        annotationProcessor 'com.google.dagger:dagger-compiler:2.4'
        compile 'com.google.dagger:dagger:2.4'
        provided 'org.glassfish:javax.annotation:10.0-b28'
        } 你好按照这种方式并不自动生成dagger啊
        zpayh:@boboyuwu 你按照文章一步一步下来,应该可以的
        boboyuwu:@zpayh 这个依赖Build都没用不生成但也不报错。。
        zpayh:什么意思?要生成文件要build一下的……
      • boboyuwu:构造方法上没法用Qualifier啊
        zpayh:@boboyuwu 不算,你可以不要在构造函数那里@Inject的,去Module那边用函数包装一下就可以了
        boboyuwu:@zpayh 这算Bug嘛? 。。假如二个不同构造, @inject 还没发Qualifier了。。。
        zpayh:@boboyuwu 是的,你说得没错……
      • 准备流浪的雪:楼主么么哒
        zpayh:@青春不迷茫_Lee https://github.com/MindorksOpenSource/android-mvp-architecture 看这个MVP架构吧,我目前正在用它的,用起来很爽
        谁的春春不迷茫:楼主写的很全面,很不错,。希望再来一篇实际项目的集成使用,配合 MVP
        zpayh:嘟嘟么么哒
      • stareme:通谷易懂,感谢楼主分享,感觉大部分都理解了, 就差实践了。
        开头引入apt有点问题,现在都是直接dependencies 里"annotationProcessor 'com.google.dagger:dagger-compiler:2.0'",没apt啥事了
        zpayh:@stareme 嗯嗯,更开头已经更新了,毕竟文章写于以前,那时还是在用apt的,谢谢
      • suit_liu:受教了,理解深刻
        zpayh:@小开伴儿 您的每个评论都是作者写博客的动力哈,谢谢
      • 阿V很简单:通俗易懂,谢谢
        zpayh:@阿V很简单 不客气,谢谢点赞
      • hqzxzwb:浏览了几篇文章,看到这一篇总算明白一些了。
        另外目前annotationProcessor已经在android gradle plugin直接支持了。使用
        annotationProcessor 'com.google.dagger:dagger-compiler:2.0'
        引入即可。
        不再需要引入android-apt这个plugin。
        zpayh:是么?谢谢哈,我也不知道,多谢提醒:kissing_heart:
      • 2b3fa3a63231:非常不错,受教了。只是有个问题不明白,MainComponent的 public abstract void inject(MainActivity activity);把参数Mainactivity改为AActivity会报错
        zpayh:@2b3fa3a63231 好像是没办法,尽量避免,这个暂时我不知道如何解决,@Qualifier限定符又不能放在参数里面,Dagger2无法找到匹配的那个
        2b3fa3a63231:@zpayh :clap: 你说的没错,是这个原因。例子和你的一样,只不过将Acomponent的注入换成MainActivity。是否Acomponent从属和MainComponent依赖于ApplicationComponent后,两者产生了联系?还有就是我在module里产生一个对象,其有两个相同类型的参数,该怎么处理
        zpayh:@2b3fa3a63231 不太明白你的意思,或者说AActivity本身有没有其他注入来源?一个类不能同时接受多个Component注入的,最关键的一点,就是你要多想想此类的成员如何从Component中获取,会不会有歧义发生
      • OneBelowZero:感谢楼主 现在请教一个问题 怎么动态切换baseUrl呢?
        OneBelowZero:@zpayh 其实是可以的 已经解决了 Retrofit post全新的url替换就可以 但是Retrofit 请求的时候注意后台返回内容格式 如果是 text/html 的话 直接返回String回报错 返回object就没问题了 不过还是感谢楼主
        OneBelowZero:@zpayh 蛋疼啊 已经用了 现在有三个baseurl切换
        zpayh:@OneBelowZero 貌似直接动态切换是不行的吧,我也试过想动态更新BaseUrl,结果报错,说要为final类型的。
      • 时间在渗透:非常不错,看我之后已明白了Dagger的基本使用了...感谢
        时间在渗透:@zpayh 好的, :+1:
        zpayh:@时间在渗透 最好也去看看我后面给的参考链接,他们写的更加透彻 :yum:
      • 磐林:请教你一个问题:
        我在Module中将一个提供实例的方法A用自定义@Qualifier注解,然后Module中的提供实例的B方法的参数有用到A提供的实例。接着我在Component中暴露A、B方法,A方法同样用该自定义@Qualifier注解,编译时提示cannot be provided without an @Inject constructor or from an @Provides-annotated method.原因是因为暴露的B方法找不到它要是参数实例,也就是A方法提供的实例,可我在Module中明明有写A方法来提供这个实例,这是为什么?我将Module、Component中的自定义@Qualifier去掉或者给A实例的类型的构造方法加上@Inject,就能正常编译了,奇怪!
        磐林: @zpayh 如果是这样的话,还能说的通,我再捣鼓捣鼓,谢谢啦
        zpayh:@王小追 参数中包含带@Qualifier的类型的好像是无法找到参数实例的,被你@Qualifier的参数一般是无法出现在方法参数里面的,不知道我有没有理解错
      • 红发_SHANKS:你这是强行圈粉》。。。。。好吧,我去...差点就从入门到放弃了......
      • 红发_SHANKS:对照敲了一遍代码,然后看了3回你的文章才明白....太笨了,写的很详细,但是dagger真心麻烦....
        zpayh:@壹分Orz 主要是我上传的代码是最终版的,你如果不照着仔细看敲下来的话可能有些会出错,理解了可以去看我后面分享的大神写的文章,加深理解
      • 24k纯野生:Error:(14, 17) 错误: com.example.wjc.demo.DemoAdapter is listed as a module, but is not annotated with @Module

        这是因为什么
        24k纯野生:@zpayh 额已解决- -。。粗心了
        zpayh:@24k纯野生 额,翻译一下,你是不是少加了@Module
      • 罗雄文:我照抄了你的代码来运行,在“依赖方式”的代码就编译出错了。
        Error:(12, 26) 错误: com.google.gson.Gson is bound multiple times:
        com.google.gson.Gson com.gmail.luoxwen.trydagger2.ApplicationComponent.getGson()
        @Provides com.google.gson.Gson com.gmail.luoxwen.trydagger2.MainModule.provideGson()
        Error:(14, 26) 错误: com.google.gson.Gson is bound multiple times:
        com.google.gson.Gson com.gmail.luoxwen.trydagger2.ApplicationComponent.getGson()
        @Provides com.google.gson.Gson com.gmail.luoxwen.trydagger2.MainModule.provideGson()
        这是哪里出错了?
        zpayh:@其实就是天蝎座的天蝎座 不客气~
        670761ab7280:感谢作者的分享,这个问题确实是被绑定了多次,在MainComponent中 modules既有MainModule.class 同时 dependencies 也有ApplicationComponent.class所以会绑定了两次,去掉MainModule.class就ok了
        zpayh:@罗雄文 Gson is bound multiple times被多次绑定了,你看下同一个Component下有没有多个返回Gson的方法,如果有的话,一般要加上自定义@Qualifier,@Named之类的,我上面的例子都是一步一步运行过的,你应该是少写了或者多写了,你检查一下返回Gson的方法
      • suxm:写的不错,看了一些,迷糊了,楼主思路很清晰
        zpayh:@suxm 谢谢,可以参考后面给出的参考
      • Euterpe:讲解得不错 感谢投稿
        zpayh:@Euterpe 谢谢支持

      本文标题:Dagger2 使用详解

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