系列文章目录
文章目录
前言
上一篇文章我们了解了如何注入第三方框架到对象以及利用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的基本使用就结束了,网上还有其他大佬讲的好的,这些只是我学习过程中的一些分享,如果读者有其他高见,非常欢迎一起讨论交流