将Android进行到底之Dagger2(三)

  • Post author:
  • Post category:其他




系列文章目录


将Android进行到底之Dagger2(一)


将Android进行到底之Dagger2(二)





前言

上一篇文章我们了解了如何注入第三方框架到对象以及利用Dagger2实现单例。但是我们注入到时候都是只用了一个组件ApplicationComponent实现的注入,这样的话咱们的对象的作用域就只能固定一个,假如我们需要定义另一个作用域的对象,比如一个全局作用域的单和一个局部作用域的单例时该怎么做呢?相信聪明的读者都能想到那就是再定义一个组件,声明作用域范围,但是这样的话组件间就独立了,可能会出现重复定义的情况。这时就需要组件之间可以复用,这也是本篇文章的重点,下面咱们就看下如何实现组件之间的复用吧。




一、组件之间的依赖

在一个组件指定作用域后,就已经确定了该组件的生命周期。但是有些对象的实例可能生命周期更短,所以就需要定义新组件,而新组件和已经存在的组件则不可避免的会有复用的情况,也就睡说新组件可能会使用到原组件的部分资源,这时就需要使用组件依赖。

而实现组件依赖有两种实现方式,分别是:

(1)为原组件的@Component添加dependences参数,指定该组件依赖的新组件

(2)直接使用@SubComponent注册创建新组件,并装载到父组件中(产生关联的组件)

下面我们就看下两种方式的代码实现



二、组件依赖代码实现



1.为@Component添加dependencies参数

(1)创建一个UserModule类用于提供我们要注解生成的对象

@Module
public class UserModule {

   // @UserScope
    @Provides
    public User provideUser(){
        return new User();
    }
}

(2)定义一个新的组件UserComponent,并为@Component添加

dependencies依赖,并且指定module:

//@UserScope 可以使用作用域组件,需要注意的是,这里使用了
//相关的地方也需要使用
@Component(modules = UserModule.class,dependencies = ApplicationComponent.class)
public interface UserComponent {
    void inject(MainActivity mainActivity);
}

修改父组件,为@Component注解增加UserMoudle.class,并且需要提供复用对象的函数,也就是下面的retrofit(),apiService(),okhttpClient()方法,这些方法的名字可以自己定义。但是必须写,不写的话无法注解生成对象:

@Component(modules = {NetModule.class,UserModule.class})
public interface ApplicationComponent {
    void inject(MainActivity2 mainActivity2);
//要在子组件中使用他们的对象必须写上下面的方法
    Retrofit retrofit();
    ApiService apiService();
    OkHttpClient okhttpClient();
}

(3)使用Dagger2注入对象,在需要使用相应对象的Activity中用Dagger2的@Inject注解使用对象

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //执行注入动作,因为注入的是子组件,所以需要关联上父组件
        //才能复用父组件的资源
            DaggerUserComponent.builder()
            .applicationComponent(MyApplication
            .getApplicationComponent())
            .build().inject(this);
       // DaggerUserComponent.builder().build().inject(this);
         Log.d("zhongxj:", "User: " + mUser.toString());
         Log.d("zhongxj:","mRetrofit: " + mRetrofit.toString());
        Log.d("zhongxj:", "mApiService: " + mApiService.toString());
        Log.d("zhongxj:", "mOkHttpClient: " + mOkHttpClient.toString());
    }

运行结果:

2022-08-11 17:45:37.503 5240-5240/com.loveyoung.dagger2hilt D/zhongxj:: User: com.loveyoung.dagger2hilt.di.User@ae8223f
2022-08-11 17:45:37.503 5240-5240/com.loveyoung.dagger2hilt D/zhongxj:: mRetrofit: retrofit2.Retrofit@afff70c
2022-08-11 17:45:37.503 5240-5240/com.loveyoung.dagger2hilt D/zhongxj:: mApiService: retrofit2.Retrofit$1@b8a0855
2022-08-11 17:45:37.503 5240-5240/com.loveyoung.dagger2hilt D/zhongxj:: mOkHttpClient: okhttp3.OkHttpClient@f0da26a

使用这种方式咱们能够清楚的看到它的不足之处,那就是当子组件需要复用父组件的对象时,需要在父组件中声明提供对象的方法,

    Retrofit retrofit();
    ApiService apiService();
    OkHttpClient okhttpClient();

这就增加了代码量,而且很不优雅,所以就有了第二种实现组件依赖的方式。



2.使用@SubComponent注册创建的新组件

(1)创建一个Student类用于演示,给一个默认的名字李四

public class Student {
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    String name="李四";
    public Student(String newName) {
        this.name = newName;

    }

    public Student() {

    }
}

(2)创建一个StudentModule,并用@Module注解,提供一个Student对象

@Module
public class StudentModule {
    @Provides
    Student provideStudent(){
        return new Student();
    }
}

(3)创建一个StudentComponent并且用@Subcomponent注解表示它是一个子组件

@Subcomponent(modules = StudentModule.class)
public interface StudentComponent {

//需要定义这个Factory接口来辅助生成StudentComponent
    @Subcomponent.Factory
    interface Factory{
        StudentComponent create();
    }

    void inject(MainActivity2 mainActivity2);
}

(4)新建一个SubComponentModule类并用@Module注解,在注解参数中传入StudentComponent作为参数

@Module(subcomponents = StudentComponent.class)
public class SubComponentModule {

}

(5)在Component的module参数中加入SubComponentModule.class 并且提供一个studentComponent()方法生成StudentComponent组件。

@Component(modules={NetModule.class,SubComponentModule.class})
public interface ApplicationComponent {
    void inject(MainActivity mainActivity);

    //使用@subComponent的方式可以不用写这些
//    Retrofit retrofit();
//    ApiService apiService();
//    OkHttpClient okhttpClient();
    StudentComponent.Factory studentComponent();

}

(5)使用注解生成对象并使用

    @Inject
    Student stu;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
			MyApplication.getApplicationComponent()
						.studentComponent()
						.create()
						.inject(this);
        Log.d("zhongxj","MainActivity2: stu: 
        "+stu.getName());
    }

运行结果

2022-08-11 21:34:37.384 26477-26477/com.loveyoung.dagger2hilt D/zhongxj: MainActivity2: stu: 李四

到这里两种组件依赖的代码实现就介绍完了,但是还有一个问题,那要是需要使用两个Student的对象怎么办,读者可能会说,那简单,直接在StudentModule 中再声明一个方法生成Student对象就可以了嘛:也就是下面这样

@Module
public class StudentModule {
    @Provides
    Student provideStudent(){
        return new Student();
    }

    @Provides
    Student provideStudent2(){
        return new Student("student2");
    }
}

但是很遗憾,这样不行,因为这样框架不知道注解生成哪一个对象,所以需要加上标识,告诉框架对应的对象,也就是下面的样子

@Module
public class StudentModule {

    @Named("student1")
    //@StuQualifier1
    @Provides
    Student provideStudent(){
        return new Student();
    }

    @Named("student2")
    //@StuQualifier2
    @Provides
    Student provideStudent2(){
        return new Student("student2");
    }
}

使用注解生成对象

  @Named("student1")
   // @StuQualifier1
    @Inject
    Student stu;

    @Named("student2")
    //@StuQualifier2
    @Inject
    Student stu2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);   MyApplication.getApplicationComponent().studentComponent().create().inject(this);
        Log.d("zhongxj","MainActivity2: stu: " + stu.getName());
        Log.d("zhongxj","MainActivity2: stu2: " + stu2.getName());
    }

当然咱们也可以使用自定义的注解来解决这个问题,就像代码中注释掉的@StuQualifier2一样,他们的的实现如下:

@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface StuQualifier1 {
}

@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface StuQualifier2 {
}

运行结果

2022-08-11 22:40:19.462 31506-31506/com.loveyoung.dagger2hilt D/zhongxj: MainActivity2: stu: 李四
2022-08-11 22:40:19.462 31506-31506/com.loveyoung.dagger2hilt D/zhongxj: MainActivity2: stu2: student2



三.使用@Bind注解接口,获得其实现类的对象

我们在开发时会经常将某些操作抽象成接口,提高架构的稳定性,然后使用

AInterface mAInterface = new AImpl1();

的方式去生成接口实现类的对象,这种操作也是Java的特性之一——-多态的实现,那我们用Dagger2如何去实现这种场景呢,我们希望可以用注解@Inject 注解到接口上,让Dagger2帮我们生成对应到实例对象。步骤如下:

(1)定义一个接口

public interface AInterface {
}

(2)实现这个接口

public class BImpl1 implements AInterface{
}

(3)定义一个TestModule类,这个类需要是抽象类,因为里面需要定义一个抽象方法,然后使用@Module注解它,在类中提供生成对应实现类的接口。

@Module
public abstract class TestModule {

//AImpl1和BImpl1 都实现了AInterface,但是不能定义两个抽象函数去
//同时获得两个对象,若要获得AImpl1的实现类对象,只需要把bindAInterface参数改成bindAInterface(AImpl1 impl1)就行了
    @Binds
   public abstract AInterface bindAInterface(BImpl1 impl1);

    @Provides
   public static AImpl1 provideAImpl1(){
        return new AImpl1();
    }

    @Provides
    public static BImpl1 provideBImpl1(){
        return new BImpl1();
    }
}

(4)在ApplicationComponent组件中加载TestModule

@Component(modules = {NetModule.class, SubComponentModule.class,TestModule.class})
public interface ApplicationComponent {
    void inject(MainActivity mainActivity);
}

(5)使用注解生成对应实现类对象

public class MainActivity2 extends AppCompatActivity {
    @Inject
    AInterface mAInterface;

    @Inject
    AInterface mAInterface1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2); MyApplication.getApplicationComponent().studentComponent().create().inject(this);
        
        Log.d("zhongxj","MainActivity2: mAInterface1: " + mAInterface1.toString());
//
        Log.d("zhongxj","MainActivity2: mAInterface: " + mAInterface.toString());
    }
}

(6)运行结果

2022-08-12 07:56:38.885 18920-18920/com.loveyoung.dagger2hilt D/zhongxj: MainActivity2: mAInterface1: com.loveyoung.dagger2hilt.di.BImpl1@8898fcc
2022-08-12 07:56:38.885 18920-18920/com.loveyoung.dagger2hilt D/zhongxj: MainActivity2: mAInterface: com.loveyoung.dagger2hilt.di.BImpl1@2a9215


温馨提示:这些例子都是紧紧相连的,如果要自己动手实现下例子中的方法,需要从前面的文章中看起哦




总结

以上就是今天要讲的内容,至此,Dagger2的基本使用就结束了,网上还有其他大佬讲的好的,这些只是我学习过程中的一些分享,如果读者有其他高见,非常欢迎一起讨论交流



版权声明:本文为zxj2589原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。